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

WPDA_List_Table_Menu.php in WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards 5.5.77, at WPDataAccess/List_Table/WPDA_List_Table_Menu.php

2,655 lines 120.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Suppress "error - 0 - No summary was found for this file" on phpdoc generation
5 *
6 * @package WPDataAccess\List_Table
7 */
8 // phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing -- verified on page
9 namespace WPDataAccess\List_Table;
10
11 use WP_Data_Access_Admin;
12 use WPDataAccess\Connection\WPDADB;
13 use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Access;
14 use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Exist;
15 use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Lists;
16 use WPDataAccess\Data_Dictionary\WPDA_List_Columns_Cache;
17 use WPDataAccess\Plugin_Table_Models\WPDA_Media_Model;
18 use WPDataAccess\Plugin_Table_Models\WPDA_Table_Settings_Model;
19 use WPDataAccess\Plugin_Table_Models\WPDA_User_Menus_Model;
20 use WPDataAccess\Utilities\WPDA_Favourites;
21 use WPDataAccess\Utilities\WPDA_Import_Multi;
22 use WPDataAccess\Utilities\WPDA_Message_Box;
23 use WPDataAccess\WPDA;
24 /**
25 * Class WPDA_List_Table_Menu
26 *
27 * This class implements the Data Explorer shown in the plugin menu.
28 *
29 * WPDA_List_Table_Menu extends WPDA_List_Table. Although both list tables basically offer the same functionality
30 * WPDA_List_Table_Menu selects data from MySQL view 'information_schema.tables', where WPDA_List_Table selects
31 * data from tables that are located in the WordPress database schema. The view that serves as the 'base table'
32 * for WPDA_List_Table_Menu is not updatable. A data entry form is therefore not available for WPDA_List_Table_Menu.
33 *
34 * Export functionality word WPDA_List_Table_Menu differs from WPDA_List_Table as well. WPDA_List_Table_Menu allows
35 * to export single tables, as well as multiple tables at once as s bulk action.
36 *
37 * When the user clicks on 'view', a list table for the selected table of view is shown.
38 *
39 * @author Peter Schulz
40 * @since 1.0.0
41 */
42 class WPDA_List_Table_Menu extends WPDA_List_Table {
43 const LOADING = 'Loading...';
44
45 const WPDACHK = 'wp-data-access-premium-data-services';
46
47 /**
48 * Holds tables marked as favourite
49 *
50 * @var array|null
51 */
52 protected $favourites = null;
53
54 /**
55 * Show, hide or empty
56 *
57 * @var string|null
58 */
59 protected $wpda_main_favourites = null;
60
61 /**
62 * Indicates whether innodb_file_per_table is enabled or disabled
63 *
64 * @var bool
65 */
66 protected $innodb_file_per_table = true;
67
68 /**
69 * Can user create database
70 *
71 * @var bool
72 */
73 protected $user_can_create_db = false;
74
75 /**
76 * Hint button create db
77 *
78 * @var string
79 */
80 protected $user_create_db_hint = '';
81
82 /**
83 * Can user drop database
84 *
85 * @var bool
86 */
87 protected $user_can_drop_db = false;
88
89 /**
90 * Hint button drop db
91 *
92 * @var string
93 */
94 protected $user_drop_db_hint = '';
95
96 /**
97 * Used to switch to another schema (after create/drop db)
98 *
99 * @var null
100 */
101 protected $switch_schema_name = null;
102
103 /**
104 * WPDA_List_Table_Menu constructor
105 *
106 * Constructor calls constructor of WPDA_List_Table. Before calling constructor of WPDA_List_Table the
107 * table name is set to {@see WPDA_List_Table::LIST_BASE_TABLE} which gives us the base table for the data
108 * explorer.
109 *
110 * Column headers and columns queried are defined hardcoded as this class handles only one base table and it's
111 * columns (table specific implementation).
112 *
113 * @param array $args See {@see WPDA_List_Table::__construct()}.
114 *
115 * @see WPDA_List_Table
116 *
117 * @since 1.0.0
118 */
119 public function __construct( $args = array() ) {
120 global $wpdb;
121 $args['table_name'] = WPDA_List_Table::LIST_BASE_TABLE;
122 // Add column labels.
123 $args['column_headers'] = self::column_headers_labels();
124 $args['title'] = 'Data Explorer';
125 $args['subtitle'] = '';
126 // Define an empty subtitle to prevent WPDA_List_Table from adding one.
127 if ( isset( $_REQUEST['action'] ) ) {
128 // Process create, drop and edit table actions before calling parent constructor to allow to redirect to
129 // another database.
130 if ( 'create_db' === $_REQUEST['action'] ) {
131 $this->process_bulk_action_create_db();
132 } elseif ( 'drop_db' === $_REQUEST['action'] ) {
133 $this->process_bulk_action_drop_db();
134 } elseif ( 'edit_db' === $_REQUEST['action'] ) {
135 $this->process_bulk_action_edit_db();
136 }
137 }
138 parent::__construct( $args );
139 $this->set_columns_queried( array(
140 'table_name AS table_name',
141 'if (find_in_set(table_name,\'' . implode( ',', WPDA::get_wp_tables() ) . '\')
142 , \'' . WPDA::get_table_type_text( WPDA::TABLE_TYPE_WP ) . '\'
143 , if (find_in_set(table_name,\'' . implode( ',', WPDA::get_wpda_tables() ) . '\')
144 , \'' . WPDA::get_table_type_text( WPDA::TABLE_TYPE_WPDA ) . '\'
145 , lower(table_type)
146 )
147 ) AS table_type',
148 'create_time AS create_time',
149 'table_rows AS table_rows',
150 'auto_increment AS auto_increment',
151 'engine AS engine',
152 'data_length+index_length AS total_size',
153 'data_length AS data_size',
154 'index_length AS index_size',
155 'data_free AS overhead',
156 'table_collation AS table_collation',
157 'table_type AS table_type_db'
158 ) );
159 $this->favourites = get_option( WPDA_Favourites::FAVOURITES_OPTION_NAME );
160 if ( null === $this->switch_schema_name ) {
161 $this->schema_name = $this->get_schema_name();
162 } else {
163 $this->schema_name = $this->switch_schema_name;
164 }
165 $this->wpda_main_favourites = $this->get_favourites();
166 // Instantiate WPDA_Import.
167 $this->wpda_import = new WPDA_Import_Multi("?page={$this->page}", $this->schema_name);
168 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
169 if ( null === $wpdadb ) {
170 /* translators: %s = remote database name */
171 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->schema_name ) ) );
172 }
173 $result = $wpdadb->get_row( "show session variables like 'innodb_file_per_table'" );
174 if ( !empty( $result ) ) {
175 $this->innodb_file_per_table = 'ON' === $result->Value;
176 }
177 // Can user create/drop databases?
178 $this->user_can_create_db = WPDA_Dictionary_Access::can_create_db();
179 if ( $this->user_can_create_db ) {
180 $this->user_create_db_hint = __( 'Create local database/add remote database connection', 'wp-data-access' );
181 } else {
182 $this->user_create_db_hint = __( 'Add remote database connection (not authorized to create local database)', 'wp-data-access' );
183 }
184 $this->user_can_drop_db = WPDA_Dictionary_Access::can_drop_db();
185 if ( $this->user_can_drop_db ) {
186 if ( $wpdb->dbname === $this->schema_name ) {
187 $this->user_can_drop_db = false;
188 $this->user_drop_db_hint = __( 'Cannot drop WordPress database', 'wp-data-access' );
189 } elseif ( 'sys' === $this->schema_name || 'mysql' === $this->schema_name || 'information_schema' === $this->schema_name || 'performance_schema' === $this->schema_name || '' === $this->schema_name ) {
190 $this->user_can_drop_db = false;
191 $this->user_drop_db_hint = __( 'Cannot drop MySQL database', 'wp-data-access' );
192 } else {
193 if ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ) {
194 $this->user_drop_db_hint = __( "Delete remote database connection.\nDoes not drop the database! Just deletes the connection definition.", 'wp-data-access' );
195 } else {
196 $this->user_drop_db_hint = __( 'Drop selected database', 'wp-data-access' );
197 }
198 }
199 } else {
200 if ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ) {
201 $this->user_can_drop_db = true;
202 $this->user_drop_db_hint = __( "Delete remote database connection.\nDoes not drop the database! Just deletes the connection definition.", 'wp-data-access' );
203 } else {
204 $this->user_drop_db_hint = __( 'Cannot drop selected database [not authorized]', 'wp-data-access' );
205 }
206 }
207 }
208
209 /**
210 * Overwrite method to add structure to the row below.
211 *
212 * @param object $item Item info.
213 *
214 * @since 1.5.0
215 */
216 public function single_row( $item ) {
217 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
218 $columns_not_shown = count( (array) $hidden );
219 echo '<tr id="rownum_' . esc_attr( self::$list_number ) . '">';
220 $this->single_row_columns( $item );
221 echo '</tr>';
222 if ( $this->bulk_actions_enabled ) {
223 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>';
224 // Fake! Maintain odd/even colors.
225 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;">';
226 } else {
227 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>';
228 // Fake! Maintain odd/even colors.
229 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;">';
230 }
231 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>';
232 echo '</td></tr>';
233 }
234
235 /**
236 * Override column_default
237 *
238 * We need to override this method as our table is in fact a view and has no real columns:
239 * $this->wpda_list_columns->get_table_columns() returns no results
240 *
241 * We'll use jquery to write html forms to a container to make them accessible inside our list table.
242 * See WPDA_List_Table->column_default for further explanation.
243 *
244 * @param array $item Item info.
245 * @param string $column_name Column name.
246 *
247 * @return mixed Actions for the current row.
248 * @see WPDA_List_Table::column_default()
249 *
250 * @since 1.0.0
251 */
252 public function column_default( $item, $column_name ) {
253 if ( 'icons' === $column_name ) {
254 // Dummy column where icons are shown (favourites and row admin menu).
255 $table_name = $item['table_name'];
256 $favourite_class = 'dashicons-star-empty';
257 $favourite_title = 'Add to favourites';
258 if ( null !== $this->favourites && false !== $this->favourites ) {
259 $favourites_array = $this->favourites;
260 if ( isset( $favourites_array[$table_name] ) ) {
261 $favourite_class = 'dashicons-star-filled';
262 $favourite_title = 'Remove from favourites';
263 }
264 }
265 $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>";
266 return $favourites_menu;
267 }
268 if ( 'table_rows' === $column_name ) {
269 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) ) {
270 $settings_db = WPDA_Table_Settings_Model::query( $item['table_name'], $this->schema_name );
271 if ( isset( $settings_db[0]['wpda_table_settings'] ) && '' !== $settings_db[0]['wpda_table_settings'] ) {
272 $settings = json_decode( $settings_db[0]['wpda_table_settings'] );
273 } else {
274 $settings = (object) null;
275 }
276 $row_count_estimate = WPDA::get_row_count_estimate( $this->schema_name, $item['table_name'], $settings );
277 if ( !$row_count_estimate['do_real_count'] ) {
278 if ( $row_count_estimate['is_estimate'] ) {
279 return stripslashes( '~' . $row_count_estimate['row_count'] );
280 } else {
281 return stripslashes( $row_count_estimate['row_count'] );
282 }
283 } else {
284 return stripslashes( $this->count_rows( $item['table_name'] ) );
285 }
286 } else {
287 return stripslashes( (string) $item[$column_name] );
288 }
289 }
290 if ( 'total_size' === $column_name || 'data_size' === $column_name || 'index_size' === $column_name ) {
291 if ( '' === stripslashes( (string) $item[$column_name] ) ) {
292 return stripslashes( (string) $item[$column_name] );
293 } else {
294 if ( $item[$column_name] / (1024 * 1024) > 1 ) {
295 return number_format(
296 stripslashes( $item[$column_name] / (1024 * 1024) ),
297 2,
298 '.',
299 ','
300 ) . ' MB';
301 } elseif ( $item[$column_name] / 1024 > 1 ) {
302 return number_format(
303 $item[$column_name] / 1024,
304 2,
305 '.',
306 ','
307 ) . ' KB';
308 }
309 return number_format(
310 stripslashes( $item[$column_name] ),
311 2,
312 '.',
313 ','
314 ) . ' bytes';
315 }
316 }
317 if ( 'overhead' === $column_name ) {
318 if ( 'InnoDB' === $item['engine'] && !$this->innodb_file_per_table ) {
319 return '-';
320 } else {
321 if ( '' === stripslashes( (string) $item[$column_name] ) ) {
322 return '';
323 } else {
324 if ( $item[$column_name] > 0 && $item['data_size'] > 0 && $item[$column_name] / $item['data_size'] > 0.2 ) {
325 $msg = __( "Fragmentation for this table is high.\nConsider: Manage>Actions>Optimize", 'wp-data-access' );
326 $approx = " <span style='font-size:15px;' class='dashicons dashicons-warning wpda_tooltip' title='{$msg}' style'cursor:pointer;'></span>";
327 } else {
328 $approx = '';
329 }
330 if ( $item[$column_name] / (1024 * 1024) > 1 ) {
331 return number_format(
332 stripslashes( $item[$column_name] / (1024 * 1024) ),
333 2,
334 '.',
335 ','
336 ) . ' MB' . $approx;
337 } elseif ( $item[$column_name] / 1024 > 1 ) {
338 return number_format(
339 stripslashes( $item[$column_name] / 1024 ),
340 2,
341 '.',
342 ','
343 ) . ' KB' . $approx;
344 }
345 return number_format(
346 stripslashes( $item[$column_name] ),
347 2,
348 '.',
349 ','
350 ) . ' bytes' . $approx;
351 }
352 }
353 }
354 if ( 'table_name' === $column_name ) {
355 // Get table name of the current row (= key).
356 $table_name = $item[$column_name];
357 $admin_actions = array();
358 // Array containing admin actions.
359 if ( WPDA::can_manage() ) {
360 // Add manage table/view line.
361 $wp_nonce_action_table_actions = "wpda-actions-{$table_name}";
362 $wp_nonce_table_actions = wp_create_nonce( $wp_nonce_action_table_actions );
363 /* translators: %s = table name */
364 $table_actions_title = sprintf( __( 'Table %s settings', 'wp-data-access' ), $table_name );
365 $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>';
366 }
367 self::$list_number++;
368 // Prepare type checking for editing.
369 $check_view_access = 'true';
370 if ( 'on' === WPDA::get_option( WPDA::OPTION_BE_CONFIRM_VIEW ) ) {
371 if ( 'VIEW' !== strtoupper( $item['table_type_db'] ) && 'SYSTEM VIEW' !== strtoupper( $item['table_type_db'] ) ) {
372 if ( strtolower( WPDA::TABLE_TYPE_WP ) === strtolower( $item['table_type'] ) ) {
373 // WordPress table.
374 $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' );
375 } elseif ( strtolower( WPDA::TABLE_TYPE_WPDA ) === strtolower( $item['table_type'] ) ) {
376 // Plugin table.
377 $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' );
378 } else {
379 // User table (other than WordPress or plugin).
380 $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' );
381 }
382 $check_view_access = 'confirm(\'' . $msg . '\')';
383 }
384 }
385 $esc_attr = 'esc_attr';
386 $form_name = 'explore_' . self::$list_number;
387 $url = "?page={$this->page}";
388 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
389 $form = <<<EOT
390 \t\t\t\t<form id='{$esc_attr( $form_name )}' action='{$esc_attr( $url )}' method='post'>
391 \t\t\t\t\t<input type='hidden' name='wpdaschema_name' value='{$esc_attr( $this->schema_name )}' />
392 \t\t\t\t\t<input type='hidden' name='table_name' value='{$esc_attr( $table_name )}' />
393 \t\t\t\t\t<input type='hidden' name='action' value='listtable' />
394 \t\t\t\t</form>
395 EOT;
396 $explore = str_replace( array("\n", "\r"), '', $form );
397 ?>
398
399 <script type='text/javascript'>
400 jQuery("#wpda_invisible_container").append("<?php
401 // phpcs:disable WordPress.Security.EscapeOutput
402 echo $explore;
403 // phpcs:enable WordPress.Security.EscapeOutput
404 ?>");
405 </script>
406
407 <?php
408 $action_view = ' <i class="fas fa-table-list wpda_icon_on_button"></i> ' . __( 'Explore', 'wp-data-access' );
409 /* translators: %s = tablename */
410 $table_view_title = sprintf( __( 'Explore %s table', 'wp-data-access' ), $table_name );
411 $actions['wpda_listtable'] = sprintf(
412 '<a href="javascript:void(0)"
413 title="%s"
414 class="view wpda_tooltip"
415 onclick="if (%s) jQuery(\'#%s\').submit()">
416 %s
417 </a>',
418 $table_view_title,
419 $check_view_access,
420 $form_name,
421 $action_view
422 );
423 if ( strtolower( WPDA::TABLE_TYPE_WP ) === strtolower( $item['table_type'] ) ) {
424 ?>
425 <script type='text/javascript'>
426 wpda_bulk.push('<?php
427 echo esc_attr( $table_name );
428 ?>');
429 </script>
430 <?php
431 }
432 if ( strtolower( WPDA::TABLE_TYPE_WP ) !== strtolower( $item['table_type'] ) && strtolower( WPDA::TABLE_TYPE_WPDA ) !== strtolower( $item['table_type'] ) ) {
433 // Validate schema, table and column names
434 $warning = WPDA::validate_names( $this->schema_name, $table_name );
435 } else {
436 $warning = '';
437 }
438 /* translators: 1: warning; 2: row action; 3: admin action */
439 return sprintf(
440 '%1$s %2$s %3$s',
441 $table_name . $warning,
442 "<span class=\"nobr\">{$this->row_actions( $actions )}</span>",
443 "<span id=\"span_admin_menu_{$table_name}\" class=\"nobr\" style=\"display:none;width:auto;float:clear;\">{$this->row_actions( $admin_actions )}</span>"
444 );
445 }
446 if ( 'create_time' === $column_name ) {
447 if ( null !== $item[$column_name] ) {
448 return date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $item[$column_name] ) );
449 } else {
450 return '';
451 }
452 }
453 return stripslashes( (string) $item[$column_name] );
454 }
455
456 /**
457 * Count the number of rows in a table.
458 *
459 * This method is used to get the number of rows in an InnoDB table as the table_rows column of
460 * information_schema.tables only return an estimate.
461 *
462 * @param string $table_name Database table name.
463 *
464 * @return integer|null Number of rows in $table_name.
465 * @since 1.5.1
466 */
467 protected function count_rows( $table_name ) {
468 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
469 if ( null === $wpdadb ) {
470 /* translators: %s = remote database name */
471 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->schema_name ) ) );
472 }
473 if ( '' === $this->schema_name ) {
474 $query = "\n\t\t\t\t\tselect count(*)\n\t\t\t\t\tfrom `{$table_name}`\n\t\t\t\t";
475 } else {
476 $query = "\n\t\t\t\t\tselect count(*)\n\t\t\t\t\tfrom `{$wpdadb->dbname}`.`{$table_name}`\n\t\t\t\t";
477 }
478 $suppress = $wpdadb->suppress_errors( true );
479 $count = @$wpdadb->get_var( $query );
480 if ( !is_numeric( $count ) ) {
481 $count = '<span class="dashicons dashicons-flag wpda_tooltip" style="color:red;padding-left:5px" title="' . str_replace( '"', "'", $wpdadb->last_error ) . '"></span>';
482 }
483 $wpdadb->suppress_errors( $suppress );
484 return $count;
485 }
486
487 /**
488 * Override get_columns
489 *
490 * @return array
491 * @see WPDA_List_Table::get_columns()
492 *
493 * @since 1.0.0
494 */
495 public function get_columns() {
496 $columns = array();
497 if ( $this->bulk_actions_enabled ) {
498 $columns = array(
499 'cb' => '<input type="checkbox" />',
500 );
501 }
502 return array_merge( $columns, $this->column_headers );
503 }
504
505 /**
506 * Override get_sortable_columns()
507 *
508 * Type is not sortable as it is not in the view.
509 *
510 * @return array
511 * @see WPDA_List_Table::get_sortable_columns()
512 *
513 * @since 1.0.0
514 */
515 public function get_sortable_columns() {
516 $columns = array();
517 $columns['table_name'] = array('table_name', false);
518 $columns['table_type'] = array('table_type', false);
519 $columns['engine'] = array('engine', false);
520 $columns['create_time'] = array('create_time', false);
521 $columns['table_rows'] = array('table_rows', false);
522 $columns['auto_increment'] = array('auto_increment', false);
523 $columns['total_size'] = array('total_size', false);
524 $columns['data_size'] = array('data_size', false);
525 $columns['index_size'] = array('index_size', false);
526 $columns['overhead'] = array('overhead', false);
527 $columns['table_collation'] = array('table_collation', false);
528 return $columns;
529 }
530
531 /**
532 * Override column_cb
533 *
534 * @param array $item Column info.
535 *
536 * @return string
537 * @since 1.0.0
538 *
539 * @see WPDA_List_Table::column_cb()
540 */
541 public function column_cb( $item ) {
542 if ( !$this->bulk_actions_enabled ) {
543 // Bulk actions disabled.
544 return '';
545 }
546 if ( 'VIEW' === $item['table_type_db'] ) {
547 $title = 'title="Performs only drop actions on views"';
548 $class = 'class="wpda_tooltip"';
549 } else {
550 $title = '';
551 $class = '';
552 }
553 return "<input type='checkbox' name='bulk-selected[]' value='{$item['table_name']}' {$title} {$class} />";
554 }
555
556 /**
557 * Override get_bulk_actions
558 *
559 * @return array
560 * @see WPDA_List_Table::get_bulk_actions()
561 *
562 * @since 1.0.0
563 */
564 public function get_bulk_actions() {
565 if ( !$this->bulk_actions_enabled ) {
566 // Bulk actions disabled.
567 return '';
568 }
569 $actions = array();
570 $actions['bulk-export'] = __( 'Export Table(s)', 'wp-data-access' );
571 $actions['bulk-drop'] = __( 'Drop Table(s)/View(s) (does not drop WordPress tables)', 'wp-data-access' );
572 $actions['bulk-truncate'] = __( 'Truncate Table(s) (does not truncate WordPress tables)', 'wp-data-access' );
573 return $actions;
574 }
575
576 /**
577 * Override process_bulk_action()
578 *
579 * @since 1.0.0
580 *
581 * @see WPDA_List_Table::process_bulk_action()
582 */
583 public function process_bulk_action() {
584 switch ( $this->current_action() ) {
585 case 'bulk-export':
586 $this->process_bulk_action_bulk_export();
587 break;
588 case 'bulk-drop':
589 $this->process_bulk_action_bulk_drop();
590 break;
591 case 'bulk-truncate':
592 $this->process_bulk_action_bulk_truncate();
593 break;
594 case 'drop':
595 $this->process_bulk_action_drop();
596 break;
597 case 'truncate':
598 $this->process_bulk_action_truncate();
599 break;
600 case 'rename-table':
601 $this->process_bulk_action_rename_table();
602 break;
603 case 'copy-table':
604 $this->process_bulk_action_copy_table();
605 break;
606 case 'optimize-table':
607 $this->process_bulk_action_optimize_table();
608 break;
609 case 'refresh-table':
610 $this->process_bulk_action_refresh_table();
611 break;
612 case 'c2wp-table':
613 $this->process_bulk_action_c2wp_table();
614 break;
615 }
616 }
617
618 protected function process_bulk_action_refresh_table() {
619 }
620
621 protected function process_bulk_action_drop_db() {
622 if ( !$this->process_bulk_action_check_wpnonce( 'wpda-drop-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnoncedropdb' ) ) {
623 return;
624 }
625 if ( !isset( $_REQUEST['database'] ) ) {
626 $msg = new WPDA_Message_Box(array(
627 'message_text' => __( 'Cannot drop database [missing argument]', 'wp-data-access' ),
628 'message_type' => 'error',
629 'message_is_dismissible' => false,
630 ));
631 $msg->box();
632 return;
633 }
634 global $wpdb;
635 $database = str_replace( '`', '', sanitize_text_field( wp_unslash( $_REQUEST['database'] ) ) );
636 if ( 'rdb:' === substr( $database, 0, 4 ) ) {
637 // Delete remote database
638 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
639 if ( false === WPDADB::get_remote_database( $database ) ) {
640 $msg = new WPDA_Message_Box(array(
641 'message_text' => sprintf( __( 'Cannot delete remote database connection `%s` [remote database connection not found]', 'wp-data-access' ), $database ),
642 'message_type' => 'error',
643 'message_is_dismissible' => false,
644 ));
645 $msg->box();
646 } else {
647 if ( false === WPDADB::del_remote_database( $database ) ) {
648 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
649 $msg = new WPDA_Message_Box(array(
650 'message_text' => sprintf( __( 'Cannot delete remote database connection `%s`', 'wp-data-access' ), $database ),
651 'message_type' => 'error',
652 'message_is_dismissible' => false,
653 ));
654 $msg->box();
655 } else {
656 $msg = new WPDA_Message_Box(array(
657 'message_text' => sprintf( __( 'Remove database `%s` deleted', 'wp-data-access' ), $database ),
658 ));
659 $msg->box();
660 $this->switch_schema_name = $wpdb->dbname;
661 }
662 }
663 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
664 } else {
665 // Drop local database
666 if ( $wpdb->dbname === $database ) {
667 $msg = new WPDA_Message_Box(array(
668 'message_text' => __( 'Cannot drop WordPress database', 'wp-data-access' ),
669 'message_type' => 'error',
670 'message_is_dismissible' => false,
671 ));
672 $msg->box();
673 return;
674 }
675 if ( 'sys' === $database || 'mysql' === $database || 'information_schema' === $database || 'performance_schema' === $database || '' === $database ) {
676 $msg = new WPDA_Message_Box(array(
677 'message_text' => __( 'Cannot drop MySQL database', 'wp-data-access' ),
678 'message_type' => 'error',
679 'message_is_dismissible' => false,
680 ));
681 $msg->box();
682 return;
683 }
684 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
685 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQLPlaceholders, WordPress.DB.DirectDatabaseQuery.SchemaChange
686 if ( false === $wpdb->query( $wpdb->prepare(
687 /* translators: %s = database name */
688 'drop database `%1s`',
689 array(WPDA::remove_backticks( $database ))
690 ) ) ) {
691 // db call ok; no-cache ok.
692 $msg = new WPDA_Message_Box(array(
693 'message_text' => sprintf( __( 'Error dropping database `%s`', 'wp-data-access' ), $database ),
694 'message_type' => 'error',
695 'message_is_dismissible' => false,
696 ));
697 $msg->box();
698 } else {
699 $msg = new WPDA_Message_Box(array(
700 'message_text' => sprintf( __( 'Database `%s` dropped', 'wp-data-access' ), $database ),
701 ));
702 $msg->box();
703 $this->switch_schema_name = $wpdb->dbname;
704 }
705 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQLPlaceholders, WordPress.DB.DirectDatabaseQuery.SchemaChange
706 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
707 }
708 }
709
710 protected function process_bulk_action_edit_db() {
711 if ( !$this->process_bulk_action_check_wpnonce( 'wpda-edit-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnonceeditdb' ) ) {
712 return;
713 }
714 if ( !isset( $_REQUEST['edit_remote_database'] ) ) {
715 $msg = new WPDA_Message_Box(array(
716 'message_text' => __( 'Cannot update remote database connection [missing argument]', 'wp-data-access' ),
717 'message_type' => 'error',
718 'message_is_dismissible' => false,
719 ));
720 $msg->box();
721 return;
722 }
723 $database = sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_database'] ) );
724 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
725 $database_old = sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_database_old'] ) );
726 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
727 if ( $database !== $database_old ) {
728 // Update database connection name
729 if ( false === WPDADB::get_remote_database( $database_old ) ) {
730 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
731 $msg = new WPDA_Message_Box(array(
732 'message_text' => __( 'Cannot update remote database connection [remote database connection not found]', 'wp-data-access' ),
733 'message_type' => 'error',
734 'message_is_dismissible' => false,
735 ));
736 $msg->box();
737 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
738 return;
739 }
740 } else {
741 // Update database connection information
742 if ( false === WPDADB::get_remote_database( $database ) ) {
743 $msg = new WPDA_Message_Box(array(
744 'message_text' => __( 'Cannot update remote database connection [remote database connection not found]', 'wp-data-access' ),
745 'message_type' => 'error',
746 'message_is_dismissible' => false,
747 ));
748 $msg->box();
749 return;
750 }
751 }
752 $host = ( isset( $_REQUEST['edit_remote_host'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_host'] ) ) : '' );
753 $user = ( isset( $_REQUEST['edit_remote_user'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_user'] ) ) : '' );
754 // Cannot use sanitize_text_field on password field!
755 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
756 $passwd = ( isset( $_REQUEST['edit_remote_passwd'] ) ? wp_unslash( $_REQUEST['edit_remote_passwd'] ) : '' );
757 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
758 $port = ( isset( $_REQUEST['edit_remote_port'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_port'] ) ) : '' );
759 $schema = ( isset( $_REQUEST['edit_remote_schema'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_schema'] ) ) : '' );
760 $ssl = ( isset( $_REQUEST['edit_remote_ssl'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_ssl'] ) ) : 'off' );
761 $ssl_key = ( isset( $_REQUEST['edit_remote_client_key'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_client_key'] ) ) : '' );
762 $ssl_cert = ( isset( $_REQUEST['edit_remote_client_certificate'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_client_certificate'] ) ) : '' );
763 $ssl_ca = ( isset( $_REQUEST['edit_remote_ca_certificate'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_ca_certificate'] ) ) : '' );
764 $ssl_path = ( isset( $_REQUEST['edit_remote_certificate_path'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_certificate_path'] ) ) : '' );
765 $ssl_cipher = ( isset( $_REQUEST['edit_remote_specified_cipher'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_specified_cipher'] ) ) : '' );
766 if ( '' === $database || '' === $host || '' === $user || '' === $schema ) {
767 $msg = new WPDA_Message_Box(array(
768 'message_text' => __( 'Cannot edit remote database connection [missing arguments]', 'wp-data-access' ),
769 'message_type' => 'error',
770 'message_is_dismissible' => false,
771 ));
772 $msg->box();
773 return;
774 }
775 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
776 if ( !WPDADB::upd_remote_database(
777 $database,
778 $host,
779 $user,
780 $passwd,
781 $port,
782 $schema,
783 false,
784 $database_old,
785 $ssl,
786 $ssl_key,
787 $ssl_cert,
788 $ssl_ca,
789 $ssl_path,
790 $ssl_cipher
791 ) ) {
792 $msg = new WPDA_Message_Box(array(
793 'message_text' => sprintf( __( 'Cannot update remote database connection `%s`', 'wp-data-access' ), $database ),
794 'message_type' => 'error',
795 'message_is_dismissible' => false,
796 ));
797 $msg->box();
798 } else {
799 $msg = new WPDA_Message_Box(array(
800 'message_text' => sprintf( __( 'Remote database connection `%s` updated', 'wp-data-access' ), $database ),
801 ));
802 $msg->box();
803 if ( $database !== $database_old ) {
804 $this->switch_schema_name = $database;
805 }
806 }
807 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
808 }
809
810 protected function process_bulk_action_create_db() {
811 if ( !$this->process_bulk_action_check_wpnonce( 'wpda-create-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnoncecreatedb' ) ) {
812 return;
813 }
814 if ( isset( $_REQUEST['database_location'] ) && 'local' === $_REQUEST['database_location'] ) {
815 // Add local database
816 if ( !isset( $_REQUEST['local_database'] ) ) {
817 $msg = new WPDA_Message_Box(array(
818 'message_text' => __( 'Cannot create database [missing argument]', 'wp-data-access' ),
819 'message_type' => 'error',
820 'message_is_dismissible' => false,
821 ));
822 $msg->box();
823 return;
824 }
825 $database = str_replace( '`', '', sanitize_text_field( wp_unslash( $_REQUEST['local_database'] ) ) );
826 global $wpdb;
827 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
828 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQLPlaceholders, WordPress.DB.DirectDatabaseQuery.SchemaChange
829 if ( false === $wpdb->query( $wpdb->prepare( 'create database `%1s`', array(WPDA::remove_backticks( $database )) ) ) ) {
830 // db call ok; no-cache ok.
831 $msg = new WPDA_Message_Box(array(
832 'message_text' => sprintf( __( 'Error creating database `%s`', 'wp-data-access' ), $database ),
833 'message_type' => 'error',
834 'message_is_dismissible' => false,
835 ));
836 $msg->box();
837 } else {
838 $msg = new WPDA_Message_Box(array(
839 'message_text' => sprintf( __( 'Database `%s` created', 'wp-data-access' ), $database ),
840 ));
841 $msg->box();
842 $this->switch_schema_name = $database;
843 }
844 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQLPlaceholders, WordPress.DB.DirectDatabaseQuery.SchemaChange
845 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
846 } else {
847 // Add remote database
848 $database = ( isset( $_REQUEST['remote_database'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_database'] ) ) : '' );
849 if ( false !== WPDADB::get_remote_database( $database ) ) {
850 $msg = new WPDA_Message_Box(array(
851 'message_text' => __( 'Remote database connection already exists', 'wp-data-access' ),
852 'message_type' => 'error',
853 'message_is_dismissible' => false,
854 ));
855 $msg->box();
856 return;
857 }
858 $host = ( isset( $_REQUEST['remote_host'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_host'] ) ) : '' );
859 $user = ( isset( $_REQUEST['remote_user'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_user'] ) ) : '' );
860 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
861 $passwd = ( isset( $_REQUEST['remote_passwd'] ) ? wp_unslash( $_REQUEST['remote_passwd'] ) : '' );
862 // Cannot use sanitize_text_field on password field!
863 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
864 $port = ( isset( $_REQUEST['remote_port'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_port'] ) ) : '' );
865 $schema = ( isset( $_REQUEST['remote_schema'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_schema'] ) ) : '' );
866 $ssl = ( isset( $_REQUEST['remote_ssl'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_ssl'] ) ) : 'off' );
867 $ssl_key = ( isset( $_REQUEST['remote_client_key'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_client_key'] ) ) : '' );
868 $ssl_cert = ( isset( $_REQUEST['remote_client_certificate'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_client_certificate'] ) ) : '' );
869 $ssl_ca = ( isset( $_REQUEST['remote_ca_certificate'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_ca_certificate'] ) ) : '' );
870 $ssl_path = ( isset( $_REQUEST['remote_certificate_path'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_certificate_path'] ) ) : '' );
871 $ssl_cipher = ( isset( $_REQUEST['remote_specified_cipher'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_specified_cipher'] ) ) : '' );
872 if ( '' === $database || '' === $host || '' === $user || '' === $schema ) {
873 $msg = new WPDA_Message_Box(array(
874 'message_text' => __( 'Cannot add remote database connection [missing argument]', 'wp-data-access' ),
875 'message_type' => 'error',
876 'message_is_dismissible' => false,
877 ));
878 $msg->box();
879 return;
880 }
881 if ( 'rdb:' === $database ) {
882 $msg = new WPDA_Message_Box(array(
883 'message_text' => __( 'Invalid database name [enter a valid database name, for example rdb:remotedb]', 'wp-data-access' ),
884 'message_type' => 'error',
885 'message_is_dismissible' => false,
886 ));
887 $msg->box();
888 return;
889 }
890 if ( !WPDADB::add_remote_database(
891 $database,
892 $host,
893 $user,
894 $passwd,
895 $port,
896 $schema,
897 $ssl,
898 $ssl_key,
899 $ssl_cert,
900 $ssl_ca,
901 $ssl_path,
902 $ssl_cipher
903 ) ) {
904 $msg = new WPDA_Message_Box(array(
905 'message_text' => __( 'Cannot add remote database connection', 'wp-data-access' ),
906 'message_type' => 'error',
907 'message_is_dismissible' => false,
908 ));
909 $msg->box();
910 } else {
911 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
912 $msg = new WPDA_Message_Box(array(
913 'message_text' => sprintf( __( 'Remote database connection `%s` added', 'wp-data-access' ), $database ),
914 ));
915 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
916 $msg->box();
917 $this->switch_schema_name = $database;
918 }
919 }
920 }
921
922 /**
923 * Performs optimize table.
924 */
925 protected function process_bulk_action_optimize_table() {
926 if ( isset( $_REQUEST['optimize_table_name'] ) ) {
927 $optimize_table_name = str_replace( '`', '', sanitize_text_field( wp_unslash( $_REQUEST['optimize_table_name'] ) ) );
928 if ( $this->process_bulk_action_check_wpnonce( "wpda-optimize-{$optimize_table_name}", '_wpnonce' ) ) {
929 $dbo_type = $this->get_dbo_type( $optimize_table_name );
930 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
931 if ( false === $dbo_type || 'BASE TABLE' !== $dbo_type ) {
932 $msg = new WPDA_Message_Box(array(
933 'message_text' => sprintf( __( 'Cannot optimize `%s`', 'wp-data-access' ), $optimize_table_name ),
934 'message_type' => 'error',
935 'message_is_dismissible' => false,
936 ));
937 $msg->box();
938 } else {
939 // Optimize table.
940 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
941 if ( null === $wpdadb ) {
942 /* translators: %s = remote database name */
943 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->schema_name ) ) );
944 }
945 $wpdadb->query( "optimize table `{$optimize_table_name}`" );
946 // db call ok; no-cache ok.
947 $msg = new WPDA_Message_Box(array(
948 'message_text' => sprintf( __( 'Table `%s` optimized', 'wp-data-access' ), $optimize_table_name ),
949 ));
950 $msg->box();
951 }
952 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
953 }
954 }
955 }
956
957 /**
958 * Performs rename table/view.
959 *
960 * @since 1.6.6
961 */
962 protected function process_bulk_action_rename_table() {
963 // Check arguments.
964 if ( !$this->process_bulk_action_check_action( 'rename_table_name_old', __( 'Missing old table name', 'wp-data-access' ) ) ) {
965 return;
966 }
967 if ( $this->process_bulk_action_check_action( 'rename_table_name_new', __( 'Missing new table name', 'wp-data-access' ) ) ) {
968 // Rename table is not allowed for WordPress tables (double check).
969 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
970 $rename_table_name_old = sanitize_text_field( wp_unslash( $_REQUEST['rename_table_name_old'] ) );
971 $rename_table_name_new = sanitize_text_field( wp_unslash( $_REQUEST['rename_table_name_new'] ) );
972 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
973 /* translators: %s = table name */
974 $err_txt = ' ' . sprintf( __( '[cannot rename WordPress table `%s`]', 'wp-data-access' ), $rename_table_name_old );
975 if ( '' === $rename_table_name_old ) {
976 $msg = new WPDA_Message_Box(array(
977 'message_text' => __( 'Missing old table name value', 'wp-data-access' ),
978 ));
979 $msg->box();
980 return;
981 }
982 if ( '' === $rename_table_name_new ) {
983 $msg = new WPDA_Message_Box(array(
984 'message_text' => __( 'Missing new table name value', 'wp-data-access' ),
985 ));
986 $msg->box();
987 return;
988 }
989 if ( $this->process_bulk_action_check_is_wp_table( $rename_table_name_old, $err_txt ) ) {
990 // Check if table exists.
991 if ( $this->process_bulk_action_check_table_exists( $rename_table_name_old ) ) {
992 // Check if rename is allowed.
993 if ( $this->process_bulk_action_check_wpnonce( "wpda-rename-{$rename_table_name_old}", '_wpnonce' ) ) {
994 $dbo_type = $this->get_dbo_type( $rename_table_name_old );
995 if ( false === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) {
996 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
997 $msg = new WPDA_Message_Box(array(
998 'message_text' => sprintf( __( 'Cannot rename `%s`', 'wp-data-access' ), $rename_table_name_old ),
999 'message_type' => 'error',
1000 'message_is_dismissible' => false,
1001 ));
1002 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
1003 $msg->box();
1004 } else {
1005 // Rename table/view.
1006 if ( false === $this->rename_table( $rename_table_name_old, $rename_table_name_new ) ) {
1007 $msg = new WPDA_Message_Box(array(
1008 'message_text' => __( 'Cannot rename', 'wp-data-access' ) . ' ' . strtolower( $dbo_type ) . ' `' . $rename_table_name_old . '`',
1009 'message_type' => 'error',
1010 'message_is_dismissible' => false,
1011 ));
1012 $msg->box();
1013 } else {
1014 $msg = new WPDA_Message_Box(array(
1015 '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 . '` ',
1016 ));
1017 $msg->box();
1018 }
1019 }
1020 }
1021 }
1022 }
1023 }
1024 }
1025
1026 /**
1027 * Rename table/view.
1028 *
1029 * @param string $rename_table_name_old Old table name.
1030 * @param string $rename_table_name_new New table name.
1031 *
1032 * @return false|int
1033 * @since 1.6.6
1034 */
1035 protected function rename_table( $rename_table_name_old, $rename_table_name_new ) {
1036 if ( WPDA::is_wp_table( $rename_table_name_old ) ) {
1037 // Never ever allow renaming a WordPress table!
1038 $msg = new WPDA_Message_Box(array(
1039 'message_text' => __( 'Not authorized [cannot rename WordPress tables]', 'wp-data-access' ),
1040 'message_type' => 'error',
1041 'message_is_dismissible' => false,
1042 ));
1043 $msg->box();
1044 return false;
1045 }
1046 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
1047 if ( null === $wpdadb ) {
1048 /* translators: %s = remote database name */
1049 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->schema_name ) ) );
1050 }
1051 return $wpdadb->query( 'rename table `' . str_replace( '`', '', $rename_table_name_old ) . '` to `' . str_replace( '`', '', $rename_table_name_new ) . '`' );
1052 // db call ok; no-cache ok.
1053 }
1054
1055 /**
1056 * Performs copying a table.
1057 *
1058 * @since 1.6.6
1059 */
1060 protected function process_bulk_action_copy_table() {
1061 // Check arguments.
1062 if ( !$this->process_bulk_action_check_action( 'copy_table_name_src', __( 'Missing source table name', 'wp-data-access' ) ) ) {
1063 return;
1064 }
1065 if ( $this->process_bulk_action_check_action( 'copy_table_name_dst', __( 'Missing destination table name', 'wp-data-access' ) ) ) {
1066 // copy table is not allowed for WordPress tables (double check).
1067 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
1068 $copy_schema_name_src = sanitize_text_field( wp_unslash( $_REQUEST['copy_schema_name_src'] ) );
1069 $copy_table_name_src = sanitize_text_field( wp_unslash( $_REQUEST['copy_table_name_src'] ) );
1070 $copy_schema_name_dst = sanitize_text_field( wp_unslash( $_REQUEST['copy_schema_name_dst'] ) );
1071 $copy_table_name_dst = sanitize_text_field( wp_unslash( $_REQUEST['copy_table_name_dst'] ) );
1072 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
1073 if ( '' === $copy_schema_name_src ) {
1074 $msg = new WPDA_Message_Box(array(
1075 'message_text' => __( 'Missing source schema name', 'wp-data-access' ),
1076 ));
1077 $msg->box();
1078 return;
1079 }
1080 if ( '' === $copy_table_name_src ) {
1081 $msg = new WPDA_Message_Box(array(
1082 'message_text' => __( 'Missing source table name', 'wp-data-access' ),
1083 ));
1084 $msg->box();
1085 return;
1086 }
1087 if ( '' === $copy_schema_name_dst ) {
1088 $msg = new WPDA_Message_Box(array(
1089 'message_text' => __( 'Missing destination schema name', 'wp-data-access' ),
1090 ));
1091 $msg->box();
1092 return;
1093 }
1094 if ( '' === $copy_table_name_dst ) {
1095 $msg = new WPDA_Message_Box(array(
1096 'message_text' => __( 'Missing destination table name', 'wp-data-access' ),
1097 ));
1098 $msg->box();
1099 return;
1100 }
1101 // Check if table exists.
1102 if ( $this->process_bulk_action_check_table_exists( $copy_table_name_src ) ) {
1103 // Check if copy is allowed.
1104 if ( $this->process_bulk_action_check_wpnonce( "wpda-copy-{$copy_table_name_src}", '_wpnonce' ) ) {
1105 $dbo_type = $this->get_dbo_type( $copy_table_name_src );
1106 if ( false === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) {
1107 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
1108 $msg = new WPDA_Message_Box(array(
1109 'message_text' => sprintf( __( 'Cannot copy `%s`', 'wp-data-access' ), $copy_table_name_src ),
1110 'message_type' => 'error',
1111 'message_is_dismissible' => false,
1112 ));
1113 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
1114 $msg->box();
1115 } else {
1116 $include_data = ( isset( $_REQUEST['copy-table-data'] ) ? 'on' : 'off' );
1117 // Copy table/view.
1118 if ( false === $this->copy_table(
1119 $copy_schema_name_src,
1120 $copy_table_name_src,
1121 $copy_schema_name_dst,
1122 $copy_table_name_dst,
1123 $include_data
1124 ) ) {
1125 $msg = new WPDA_Message_Box(array(
1126 'message_text' => __( 'Cannot copy', 'wp-data-access' ) . ' ' . strtolower( $dbo_type ) . ' ' . $copy_table_name_src,
1127 'message_type' => 'error',
1128 'message_is_dismissible' => false,
1129 ));
1130 $msg->box();
1131 } else {
1132 $msg = new WPDA_Message_Box(array(
1133 '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}` ",
1134 ));
1135 $msg->box();
1136 }
1137 }
1138 }
1139 }
1140 }
1141 }
1142
1143 /**
1144 * Copy table.
1145 *
1146 * @param string $copy_schema_name_src Source schema name.
1147 * @param string $copy_table_name_src Source table name.
1148 * @param string $copy_schema_name_dst Destination schema name.
1149 * @param string $copy_table_name_dst Destination table name.
1150 * @param string $include_data 'on' = include data.
1151 *
1152 * @return false|int
1153 * @since 1.6.6
1154 */
1155 protected function copy_table(
1156 $copy_schema_name_src,
1157 $copy_table_name_src,
1158 $copy_schema_name_dst,
1159 $copy_table_name_dst,
1160 $include_data
1161 ) {
1162 $copy_schema_name_src = WPDA::remove_backticks( $copy_schema_name_src );
1163 $copy_table_name_src = WPDA::remove_backticks( $copy_table_name_src );
1164 $copy_schema_name_dst = WPDA::remove_backticks( $copy_schema_name_dst );
1165 $copy_table_name_dst = WPDA::remove_backticks( $copy_table_name_dst );
1166 $wpdadb_src = WPDADB::get_db_connection( $copy_schema_name_src );
1167 if ( null === $wpdadb_src ) {
1168 return false;
1169 }
1170 $wpdadb_dst = WPDADB::get_db_connection( $copy_schema_name_dst );
1171 if ( null === $wpdadb_dst ) {
1172 return false;
1173 }
1174 $suppress_wpdadb_src = $wpdadb_src->suppress_errors;
1175 $wpdadb_src->suppress_errors = true;
1176 $suppress_wpdadb_dst = $wpdadb_dst->suppress_errors;
1177 $wpdadb_dst->suppress_errors = true;
1178 // Get create table statement from $wpdadb_src.
1179 // NO_TABLE_OPTIONS is deprecated in V8
1180 // $wpdadb_src->query( "SET sql_mode = 'NO_TABLE_OPTIONS'" );
1181 $query = "show create table `{$copy_table_name_src}`";
1182 $ctcmd = $wpdadb_src->get_results( $query, 'ARRAY_A' );
1183 if ( '' !== $wpdadb_src->last_error || !isset( $ctcmd[0]['Create Table'] ) ) {
1184 $wpdadb_src->suppress_errors( $suppress_wpdadb_src );
1185 $wpdadb_dst->suppress_errors( $suppress_wpdadb_dst );
1186 return false;
1187 }
1188 $create_table_statement = $ctcmd[0]['Create Table'];
1189 if ( $copy_table_name_src !== $copy_table_name_dst ) {
1190 // Modify create table statement
1191 $pos = strpos( $create_table_statement, $copy_table_name_src );
1192 if ( $pos !== false ) {
1193 $create_table_statement = substr_replace(
1194 $create_table_statement,
1195 $copy_table_name_dst,
1196 $pos,
1197 strlen( $copy_table_name_src )
1198 );
1199 }
1200 }
1201 // Create new table in $wpdadb_dst
1202 $wpdadb_dst->query( $create_table_statement );
1203 if ( '' !== $wpdadb_dst->last_error ) {
1204 $wpdadb_src->suppress_errors( $suppress_wpdadb_src );
1205 $wpdadb_dst->suppress_errors( $suppress_wpdadb_dst );
1206 return false;
1207 }
1208 if ( 'on' !== $include_data ) {
1209 return true;
1210 }
1211 if ( $copy_schema_name_src === $copy_schema_name_dst ) {
1212 // No need for buffering if source database === destination database
1213 $result = $wpdadb_dst->query( "insert `{$copy_table_name_dst}` select * from `{$copy_table_name_src}`" );
1214 // db call ok; no-cache ok.
1215 $wpdadb_src->suppress_errors( $suppress_wpdadb_src );
1216 $wpdadb_dst->suppress_errors( $suppress_wpdadb_dst );
1217 return $result;
1218 }
1219 // Check if buffering is needed for this table
1220 $settings_db = WPDA_Table_Settings_Model::query( $copy_table_name_src, $copy_schema_name_src );
1221 if ( isset( $settings_db[0]['wpda_table_settings'] ) ) {
1222 $settings_db_custom = json_decode( $settings_db[0]['wpda_table_settings'] );
1223 if ( isset( $settings_db_custom->table_settings->query_buffer_size ) ) {
1224 $query_buffer_size = $settings_db_custom->table_settings->query_buffer_size;
1225 } else {
1226 $query_buffer_size = 0;
1227 }
1228 } else {
1229 $query_buffer_size = 0;
1230 }
1231 // Copy table data from $wpdadb to $wpdb
1232 $query = "select * from `{$copy_table_name_src}`";
1233 if ( is_numeric( $query_buffer_size ) && $query_buffer_size > 0 ) {
1234 // phpcs:disable Squiz.PHP.DiscouragedFunctions.Discouraged
1235 set_time_limit( 0 );
1236 // phpcs:enable Squiz.PHP.DiscouragedFunctions.Discouraged
1237 ?>
1238 <p class="wpda_pds_msg_create">
1239 Copy table `<?php
1240 echo esc_attr( $copy_schema_name_src );
1241 ?>`.`<?php
1242 echo esc_attr( $copy_table_name_src );
1243 ?>`
1244 </p>
1245 <p class="wpda_pds_msg_create">
1246 To `<?php
1247 echo esc_attr( $copy_schema_name_dst );
1248 ?>`.`<?php
1249 echo esc_attr( $copy_table_name_dst );
1250 ?>`
1251 </p>
1252 <p class="wpda_pds_msg_create">
1253 Hang on...
1254 </p>
1255 <p class="wpda_pds_msg_create">
1256 Table `<?php
1257 echo esc_attr( $copy_table_name_dst );
1258 ?>` created...
1259 </p>
1260 <p class="wpda_pds_msg_create">
1261 Copying data...
1262 </p>
1263 <?php
1264 ob_flush();
1265 flush();
1266 // Create array for fast column_name based access.
1267 $wpda_list_columns = WPDA_List_Columns_Cache::get_list_columns( $copy_schema_name_src, $copy_table_name_src );
1268 $table_columns = $wpda_list_columns->get_table_columns();
1269 $column_data_types = array();
1270 foreach ( $table_columns as $column_value ) {
1271 $column_data_types[$column_value['column_name']] = $column_value['data_type'];
1272 }
1273 $i = 0;
1274 $total = 0;
1275 $sql = $query . ' limit ' . $query_buffer_size;
1276 $rows = $wpdadb_src->get_results( $sql, 'ARRAY_A' );
1277 while ( $wpdadb_src->num_rows > 0 ) {
1278 $total += $wpdadb_src->num_rows;
1279 foreach ( $rows as $row ) {
1280 $wpdadb_dst->insert( $copy_table_name_dst, $row );
1281 $wpdadb_dst->flush();
1282 $wpdadb_dst->queries = null;
1283 }
1284 echo '<script>jQuery("p.wpda_pds_msg").remove();</script>';
1285 echo '<p class="wpda_pds_msg">Copied ' . number_format( $total ) . ' rows...</p>';
1286 ob_flush();
1287 flush();
1288 $i++;
1289 $wpdadb_src->flush();
1290 $wpdadb_src->queries = null;
1291 $sql = $query . ' limit ' . $query_buffer_size . ' offset ' . $i * $query_buffer_size;
1292 $rows = $wpdadb_src->get_results( $sql, 'ARRAY_A' );
1293 }
1294 echo '<script>jQuery("p.wpda_pds_msg_create, p.wpda_pds_msg").css("display", "none");</script>';
1295 } else {
1296 $rows = $wpdadb_src->get_results( $query, 'ARRAY_A' );
1297 foreach ( $rows as $row ) {
1298 $wpdadb_dst->insert( $copy_table_name_dst, $row );
1299 }
1300 }
1301 $wpdadb_src->suppress_errors( $suppress_wpdadb_src );
1302 $wpdadb_dst->suppress_errors( $suppress_wpdadb_dst );
1303 return true;
1304 }
1305
1306 /**
1307 * Performs bulk export.
1308 *
1309 * @since 1.5.0
1310 */
1311 protected function process_bulk_action_bulk_export() {
1312 // Check is there is anything to export.
1313 if ( $this->process_bulk_action_check_action( 'bulk-selected', __( 'Empty bulk selected', 'wp-data-access' ) ) ) {
1314 // Check if export is allowed.
1315 if ( $this->process_bulk_action_check_wpnonce( 'wpda-export-' . json_encode( $this->table_name ), '_wpnonce' ) ) {
1316 // Get arguments.
1317 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
1318 $bulk_tabs = ( isset( $_REQUEST['bulk-selected'] ) ? $_REQUEST['bulk-selected'] : '' );
1319 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
1320 $wp_nonce = wp_create_nonce( 'wpda-export-' . WPDA::get_current_user_login() );
1321 $cnt = 0;
1322 $selected_tables = array();
1323 foreach ( $bulk_tabs as $table_name ) {
1324 $export_table_name = sanitize_text_field( wp_unslash( $table_name ) );
1325 // input var okay.
1326 /* translators: %s = table name */
1327 $err_txt = ' ' . sprintf( __( '[table `%s`]', 'wp-data-access' ), $export_table_name );
1328 if ( $this->process_bulk_action_check_table_exists( $export_table_name, $err_txt ) ) {
1329 $dbo_type = $this->get_dbo_type( $export_table_name );
1330 if ( false === $dbo_type || 'VIEW' === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) {
1331 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
1332 $msg = new WPDA_Message_Box(array(
1333 'message_text' => sprintf( __( 'Cannot export `%s`', 'wp-data-access' ), $export_table_name ),
1334 'message_type' => 'error',
1335 'message_is_dismissible' => false,
1336 ));
1337 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
1338 $msg->box();
1339 } else {
1340 $selected_tables[] = $export_table_name;
1341 $cnt++;
1342 }
1343 }
1344 }
1345 if ( 0 < $cnt ) {
1346 ?>
1347 <script type="application/javascript">
1348 jQuery(function() {
1349 wpda_main_export(
1350 '<?php
1351 echo esc_attr( $this->schema_name );
1352 ?>',
1353 '<?php
1354 echo esc_attr( $wp_nonce );
1355 ?>',
1356 '<?php
1357 echo json_encode( $selected_tables );
1358 ?>'
1359 );
1360 });
1361 </script>
1362 <?php
1363 }
1364 }
1365 }
1366 }
1367
1368 /**
1369 * Checks request argument needed for (bulk) action to be performed.
1370 *
1371 * @param string $argument_name Request argument name.
1372 * @param string $msg Message on failure.
1373 *
1374 * @return bool TRUE = argument found in request.
1375 * @since 1.5.0
1376 */
1377 protected function process_bulk_action_check_action( $argument_name, $msg ) {
1378 if ( !isset( $_REQUEST[$argument_name] ) ) {
1379 // Nothing export.
1380 $msg = new WPDA_Message_Box(array(
1381 'message_text' => $msg,
1382 ));
1383 $msg->box();
1384 return false;
1385 }
1386 return true;
1387 }
1388
1389 /**
1390 * Checks wpnonce against a specific action.
1391 *
1392 * @param string $wp_nonce_action Nonce action.
1393 * @param string $wp_nonce_arg Nonce argument.
1394 *
1395 * @return bool TRUE = action allowed.
1396 * @since 1.5.0
1397 */
1398 protected function process_bulk_action_check_wpnonce( $wp_nonce_action, $wp_nonce_arg ) {
1399 $wp_nonce = ( isset( $_REQUEST[$wp_nonce_arg] ) ? sanitize_text_field( wp_unslash( $_REQUEST[$wp_nonce_arg] ) ) : '' );
1400 // input var okay.
1401 if ( !wp_verify_nonce( $wp_nonce, $wp_nonce_action ) ) {
1402 $msg = new WPDA_Message_Box(array(
1403 'message_text' => __( 'Not authorized', 'wp-data-access' ),
1404 'message_type' => 'error',
1405 'message_is_dismissible' => false,
1406 ));
1407 $msg->box();
1408 return false;
1409 }
1410 return true;
1411 }
1412
1413 /**
1414 * Checks if table exists.
1415 *
1416 * @param string $table_name Database table name.
1417 * @param string $err_txt Additional error text/info.
1418 *
1419 * @return bool TRUE = table exists.
1420 * @since 1.5.0
1421 */
1422 protected function process_bulk_action_check_table_exists( $table_name, $err_txt = '' ) {
1423 $wpda_dictionary = new WPDA_Dictionary_Exist($this->schema_name, $table_name);
1424 if ( !$wpda_dictionary->table_exists() ) {
1425 $msg = new WPDA_Message_Box(array(
1426 'message_text' => __( 'Not authorized [table not found]', 'wp-data-access' ) . $err_txt,
1427 'message_type' => 'error',
1428 'message_is_dismissible' => false,
1429 ));
1430 $msg->box();
1431 return false;
1432 }
1433 return true;
1434 }
1435
1436 /**
1437 * Get database object type (VIEW, BASE_TABLE, SYSTEM VIEW).
1438 *
1439 * @param string $dbo_name Table or view name.
1440 *
1441 * @return string|boolean Database object type or false.
1442 * @since 1.5.0
1443 */
1444 protected function get_dbo_type( $dbo_name ) {
1445 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
1446 if ( null === $wpdadb ) {
1447 return false;
1448 }
1449 $query = $wpdadb->prepare( '
1450 SELECT table_type AS table_type
1451 FROM information_schema.tables
1452 WHERE table_schema = %s
1453 AND table_name = %s
1454 ', array($wpdadb->dbname, $dbo_name) );
1455 // db call ok; no-cache ok.
1456 $result = $wpdadb->get_results( $query, 'ARRAY_A' );
1457 if ( 1 === $wpdadb->num_rows ) {
1458 return $result[0]['table_type'];
1459 } else {
1460 return false;
1461 }
1462 }
1463
1464 /**
1465 * Performs a drop table or view
1466 *
1467 * Method does not drop WordPress tables.
1468 */
1469 protected function process_bulk_action_bulk_drop() {
1470 // Check is there is anything to drop.
1471 if ( $this->process_bulk_action_check_action( 'bulk-selected', __( 'Empty bulk selected', 'wp-data-access' ) ) ) {
1472 // Check if drop is allowed.
1473 if ( $this->process_bulk_action_check_wpnonce( 'wpda-drop-' . WPDA::get_current_user_login(), '_wpnonce3' ) ) {
1474 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
1475 $bulk_tabs = ( isset( $_REQUEST['bulk-selected'] ) ? $_REQUEST['bulk-selected'] : '' );
1476 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
1477 foreach ( $bulk_tabs as $table_name ) {
1478 // Drop table is not allowed for WordPress tables (double check).
1479 $drop_table_name = sanitize_text_field( wp_unslash( $table_name ) );
1480 // input var okay.
1481 /* translators: %s = table name */
1482 $err_txt = ' ' . sprintf( __( '[cannot drop WordPress table `%s`]', 'wp-data-access' ), $drop_table_name );
1483 if ( $this->process_bulk_action_check_is_wp_table( $drop_table_name, $err_txt ) ) {
1484 // Check if table exists.
1485 /* translators: %s = table name */
1486 $err_txt = ' ' . sprintf( __( '[table `%s`]', 'wp-data-access' ), $drop_table_name );
1487 if ( $this->process_bulk_action_check_table_exists( $drop_table_name, $err_txt ) ) {
1488 $dbo_type = $this->get_dbo_type( $drop_table_name );
1489 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
1490 if ( false === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) {
1491 $msg = new WPDA_Message_Box(array(
1492 'message_text' => sprintf( __( 'Cannot drop `%s`', 'wp-data-access' ), $drop_table_name ),
1493 'message_type' => 'error',
1494 'message_is_dismissible' => false,
1495 ));
1496 $msg->box();
1497 } else {
1498 if ( 'VIEW' === $dbo_type ) {
1499 // Drop view.
1500 if ( $this->drop_view( $drop_table_name ) ) {
1501 $msg = new WPDA_Message_Box(array(
1502 'message_text' => sprintf( __( 'View `%s` dropped', 'wp-data-access' ), $drop_table_name ),
1503 ));
1504 $msg->box();
1505 }
1506 } else {
1507 // Drop table.
1508 if ( $this->drop_table( $drop_table_name ) ) {
1509 $msg = new WPDA_Message_Box(array(
1510 'message_text' => sprintf( __( 'Table `%s` dropped', 'wp-data-access' ), $drop_table_name ),
1511 ));
1512 $msg->box();
1513 $this->post_drop_table( $drop_table_name );
1514 }
1515 }
1516 }
1517 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
1518 }
1519 }
1520 }
1521 }
1522 }
1523 }
1524
1525 /**
1526 * Silently delete table related info from repository (called after drop table)
1527 *
1528 * @param string $drop_table_name Database table name
1529 */
1530 protected function post_drop_table( $drop_table_name ) {
1531 global $wpdb;
1532 $suppress = $wpdb->suppress_errors( true );
1533 // Table settings...
1534 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQLPlaceholders, WordPress.DB.DirectDatabaseQuery.SchemaChange
1535 $wpdb->query( $wpdb->prepare( 'delete from `%1s` where wpda_schema_name = %s and wpda_table_name = %s ', array(WPDA::remove_backticks( WPDA_Table_Settings_Model::get_base_table_name() ), $this->schema_name, $drop_table_name) ) );
1536 // WordPress media library columns...
1537 $wpdb->query( $wpdb->prepare( 'delete from `%1s` where media_schema_name = %s and media_table_name = %s ', array(WPDA::remove_backticks( WPDA_Media_Model::get_base_table_name() ), $this->schema_name, $drop_table_name) ) );
1538 // Data menus...
1539 $wpdb->query( $wpdb->prepare( 'delete from `%1s` where menu_schema_name = %s and menu_table_name = %s ', array(WPDA::remove_backticks( WPDA_User_Menus_Model::get_base_table_name() ), $this->schema_name, $drop_table_name) ) );
1540 $wpdb->suppress_errors( $suppress );
1541 }
1542
1543 /**
1544 * Checks if table is a WordPress table.
1545 *
1546 * @param string $table_name Database table name.
1547 * @param string $err_txt Additional error text/info.
1548 *
1549 * @return bool TRUE = table is WordPress table.
1550 * @since 1.5.0
1551 */
1552 protected function process_bulk_action_check_is_wp_table( $table_name, $err_txt = '' ) {
1553 if ( WPDA::is_wp_table( $table_name ) ) {
1554 $msg = new WPDA_Message_Box(array(
1555 'message_text' => __( 'Not authorized', 'wp-data-access' ) . $err_txt,
1556 'message_type' => 'error',
1557 'message_is_dismissible' => false,
1558 ));
1559 $msg->box();
1560 return false;
1561 }
1562 return true;
1563 }
1564
1565 /**
1566 * Performs drop view.
1567 *
1568 * @param string $view_name Database view name.
1569 *
1570 * @return bool TRUE = view dropped.
1571 * @since 1.5.0
1572 */
1573 protected function drop_view( $view_name ) {
1574 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
1575 if ( null === $wpdadb ) {
1576 /* translators: %s = remote database name */
1577 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->schema_name ) ) );
1578 }
1579 return $wpdadb->query( 'drop view `' . str_replace( '`', '', $view_name ) . '`' );
1580 // db call ok; no-cache ok.
1581 }
1582
1583 /**
1584 * Performs drop table.
1585 *
1586 * @param string $table_name Database table name.
1587 *
1588 * @return bool TRUE = table dropped.
1589 * @since 1.5.0
1590 */
1591 protected function drop_table( $table_name ) {
1592 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
1593 if ( null === $wpdadb ) {
1594 /* translators: %s = remote database name */
1595 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->schema_name ) ) );
1596 }
1597 if ( WPDA::is_wp_table( $table_name ) ) {
1598 // Never ever allow dropping a WordPress table!
1599 $msg = new WPDA_Message_Box(array(
1600 'message_text' => __( 'Not authorized [cannot drop WordPress tables]', 'wp-data-access' ),
1601 'message_type' => 'error',
1602 'message_is_dismissible' => false,
1603 ));
1604 $msg->box();
1605 return false;
1606 }
1607 return $wpdadb->query( 'drop table `' . str_replace( '`', '', $table_name ) . '`' );
1608 // db call ok; no-cache ok.
1609 }
1610
1611 /**
1612 * Performs a truncate table
1613 *
1614 * Method does not truncate WordPress tables.
1615 */
1616 protected function process_bulk_action_bulk_truncate() {
1617 // Check is there is anything to truncate.
1618 if ( $this->process_bulk_action_check_action( 'bulk-selected', __( 'No table defined', 'wp-data-access' ) ) ) {
1619 // Check if truncate is allowed.
1620 if ( $this->process_bulk_action_check_wpnonce( 'wpda-truncate-' . WPDA::get_current_user_login(), '_wpnonce4' ) ) {
1621 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
1622 $bulk_tabs = ( isset( $_REQUEST['bulk-selected'] ) ? $_REQUEST['bulk-selected'] : '' );
1623 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
1624 foreach ( $bulk_tabs as $table_name ) {
1625 // Truncate table is not allowed for WordPress tables (double check).
1626 $truncate_table_name = sanitize_text_field( wp_unslash( $table_name ) );
1627 // input var okay.
1628 /* translators: %s = table name */
1629 $err_txt = ' ' . sprintf( __( '[cannot truncate WordPress table `%s`]', 'wp-data-access' ), $truncate_table_name );
1630 if ( $this->process_bulk_action_check_is_wp_table( $truncate_table_name, $err_txt ) ) {
1631 // Check if table exists.
1632 /* translators: %s = table name */
1633 $err_txt = ' ' . sprintf( __( '[table `%s`]', 'wp-data-access' ), $truncate_table_name );
1634 if ( $this->process_bulk_action_check_table_exists( $truncate_table_name, $err_txt ) ) {
1635 $dbo_type = $this->get_dbo_type( $truncate_table_name );
1636 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
1637 if ( false === $dbo_type || 'VIEW' === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) {
1638 $msg = new WPDA_Message_Box(array(
1639 'message_text' => sprintf( __( 'Cannot truncate `%s`', 'wp-data-access' ), $truncate_table_name ),
1640 'message_type' => 'error',
1641 'message_is_dismissible' => false,
1642 ));
1643 $msg->box();
1644 } else {
1645 // Truncate table.
1646 if ( $this->truncate_table( $truncate_table_name ) ) {
1647 $msg = new WPDA_Message_Box(array(
1648 'message_text' => sprintf( __( 'Table `%s` truncated', 'wp-data-access' ), $truncate_table_name ),
1649 ));
1650 $msg->box();
1651 }
1652 }
1653 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
1654 }
1655 }
1656 }
1657 }
1658 }
1659 }
1660
1661 /**
1662 * Performs truncate table.
1663 *
1664 * @param string $table_name Database table name.
1665 *
1666 * @return bool TRUE = table truncated.
1667 * @since 1.5.0
1668 */
1669 protected function truncate_table( $table_name ) {
1670 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
1671 if ( null === $wpdadb ) {
1672 /* translators: %s = remote database name */
1673 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->schema_name ) ) );
1674 }
1675 if ( WPDA::is_wp_table( $table_name ) ) {
1676 // Never ever allow truncating a WordPress table!
1677 $msg = new WPDA_Message_Box(array(
1678 'message_text' => __( 'Not authorized [cannot truncate WordPress tables]', 'wp-data-access' ),
1679 'message_type' => 'error',
1680 'message_is_dismissible' => false,
1681 ));
1682 $msg->box();
1683 return false;
1684 }
1685 // phpcs:disable WordPress.DB.PreparedSQLPlaceholders
1686 $qry = $wpdadb->query( $wpdadb->prepare( 'truncate table `%1s`', [WPDA::remove_backticks( $table_name )] ) );
1687 // db call ok; no-cache ok.
1688 // phpcs:enable WordPress.DB.PreparedSQLPlaceholders
1689 return $qry;
1690 }
1691
1692 /**
1693 * Processes drop table request.
1694 *
1695 * @since 1.5.0
1696 */
1697 protected function process_bulk_action_drop() {
1698 // Check is there is anything to drop.
1699 if ( $this->process_bulk_action_check_action( 'drop_table_name', __( 'No table defined', 'wp-data-access' ) ) ) {
1700 // Drop table is not allowed for WordPress tables (double check).
1701 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
1702 $drop_table_name = sanitize_text_field( wp_unslash( $_REQUEST['drop_table_name'] ) );
1703 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
1704 /* translators: %s = table name */
1705 $err_txt = ' ' . sprintf( __( '[cannot drop WordPress table `%s`]', 'wp-data-access' ), $drop_table_name );
1706 if ( $this->process_bulk_action_check_is_wp_table( $drop_table_name, $err_txt ) ) {
1707 // Check if table exists.
1708 if ( $this->process_bulk_action_check_table_exists( $drop_table_name ) ) {
1709 // Check if drop is allowed.
1710 if ( $this->process_bulk_action_check_wpnonce( "wpda-drop-{$drop_table_name}", '_wpnonce' ) ) {
1711 $dbo_type = $this->get_dbo_type( $drop_table_name );
1712 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
1713 if ( false === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) {
1714 $msg = new WPDA_Message_Box(array(
1715 'message_text' => sprintf( __( 'Cannot drop `%s`', 'wp-data-access' ), $drop_table_name ),
1716 'message_type' => 'error',
1717 'message_is_dismissible' => false,
1718 ));
1719 $msg->box();
1720 } else {
1721 if ( 'VIEW' === $dbo_type ) {
1722 // Drop view.
1723 if ( $this->drop_view( $drop_table_name ) ) {
1724 $msg = new WPDA_Message_Box(array(
1725 'message_text' => sprintf( __( 'View `%s` dropped', 'wp-data-access' ), $drop_table_name ),
1726 ));
1727 $msg->box();
1728 }
1729 } else {
1730 // Drop table.
1731 if ( $this->drop_table( $drop_table_name ) ) {
1732 $msg = new WPDA_Message_Box(array(
1733 'message_text' => sprintf( __( 'Table `%s` dropped', 'wp-data-access' ), $drop_table_name ),
1734 ));
1735 $msg->box();
1736 $this->post_drop_table( $drop_table_name );
1737 }
1738 }
1739 }
1740 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
1741 }
1742 }
1743 }
1744 }
1745 }
1746
1747 /**
1748 * Processes truncate table request.
1749 *
1750 * @since 1.5.0
1751 */
1752 protected function process_bulk_action_truncate() {
1753 // Check is there is anything to truncate.
1754 if ( $this->process_bulk_action_check_action( 'truncate_table_name', __( 'No table defined', 'wp-data-access' ) ) ) {
1755 // Truncate table is not allowed for WordPress tables (double check).
1756 // phpcs:disable WordPress.Security.ValidatedSanitizedInput
1757 $truncate_table_name = sanitize_text_field( wp_unslash( $_REQUEST['truncate_table_name'] ) );
1758 // phpcs:enable WordPress.Security.ValidatedSanitizedInput
1759 /* translators: %s = table name */
1760 $err_txt = ' ' . sprintf( __( '[cannot truncate WordPress table `%s`]', 'wp-data-access' ), $truncate_table_name );
1761 if ( $this->process_bulk_action_check_is_wp_table( $truncate_table_name, $err_txt ) ) {
1762 // Check if table exists.
1763 if ( $this->process_bulk_action_check_table_exists( $truncate_table_name ) ) {
1764 // Check if truncate is allowed.
1765 if ( $this->process_bulk_action_check_wpnonce( "wpda-truncate-{$truncate_table_name}", '_wpnonce' ) ) {
1766 // Truncate table.
1767 if ( $this->truncate_table( $truncate_table_name ) ) {
1768 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
1769 $msg = new WPDA_Message_Box(array(
1770 'message_text' => sprintf( __( 'Table `%s` truncated', 'wp-data-access' ), $truncate_table_name ),
1771 ));
1772 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
1773 $msg->box();
1774 }
1775 }
1776 }
1777 }
1778 }
1779 }
1780
1781 protected function add_header_actions() {
1782 $this->add_db_containers();
1783 }
1784
1785 /**
1786 * Overwrite method: add bulk array to check ddl allowed
1787 */
1788 public function show() {
1789 ?>
1790 <script type='text/javascript'>
1791 var wpda_bulk = [];
1792
1793 function wpda_bulk_valid() {
1794 var wpda_bulk_selected_valid = true;
1795 jQuery("input[name='bulk-selected[]']:checked").each(function () {
1796 var wpda_bulk_selected = jQuery(this).val();
1797 wpda_bulk.every(function (item) {
1798 if (item === wpda_bulk_selected) {
1799 alert("Action not allowed on WordPress tables!");
1800 wpda_bulk_selected_valid = false;
1801 }
1802 });
1803 if (wpda_bulk_selected_valid === false) {
1804 return false;
1805 }
1806 });
1807 return wpda_bulk_selected_valid;
1808 }
1809
1810 function wpda_check_bulk() {
1811 action = jQuery("select[name='action']").val();
1812 action2 = jQuery("select[name='action2']").val();
1813 if (action === '-1') {
1814 if (action2 === 'bulk-drop' || action2 === 'bulk-truncate') {
1815 return wpda_bulk_valid();
1816 } else {
1817 return true;
1818 }
1819 } else {
1820 if (action === 'bulk-drop' || action === 'bulk-truncate') {
1821 return wpda_bulk_valid();
1822 } else {
1823 return true;
1824 }
1825 }
1826 }
1827 </script>
1828 <?php
1829 parent::show();
1830 ?>
1831 <script type='text/javascript'>
1832 function show_hide_column(show) {
1833 for (i = 0; i <<?php
1834 echo esc_attr( self::$list_number );
1835 ?>; i++) {
1836 if (show) {
1837 jQuery('#rownum_' + i + '_x1').attr('colspan', parseInt(jQuery('#rownum_' + i + '_x1').attr('colspan')) + 1);
1838 jQuery('#rownum_' + i + '_x2').attr('colspan', parseInt(jQuery('#rownum_' + i + '_x2').attr('colspan')) + 1);
1839 } else {
1840 jQuery('#rownum_' + i + '_x1').attr('colspan', parseInt(jQuery('#rownum_' + i + '_x1').attr('colspan')) - 1);
1841 jQuery('#rownum_' + i + '_x2').attr('colspan', parseInt(jQuery('#rownum_' + i + '_x2').attr('colspan')) - 1);
1842 }
1843 }
1844 jQuery('.wp-list-table').removeClass('fixed');
1845 }
1846
1847 jQuery(function () {
1848 jQuery("#doaction").on("click", function (e) {
1849 return wpda_check_bulk();
1850 });
1851 jQuery("#doaction2").on("click", function (e) {
1852 return wpda_check_bulk();
1853 });
1854 jQuery('#table_name-hide').on("click", function (e) {
1855 show_hide_column(jQuery('#table_name-hide').is(":checked"));
1856 });
1857 jQuery('#table_type-hide').on("click", function (e) {
1858 show_hide_column(jQuery('#table_type-hide').is(":checked"));
1859 });
1860 jQuery('#create_time-hide').on("click", function (e) {
1861 show_hide_column(jQuery('#create_time-hide').is(":checked"));
1862 });
1863 jQuery('#table_rows-hide').on("click", function (e) {
1864 show_hide_column(jQuery('#table_rows-hide').is(":checked"));
1865 });
1866 jQuery('#auto_increment-hide').on("click", function (e) {
1867 show_hide_column(jQuery('#auto_increment-hide').is(":checked"));
1868 });
1869 jQuery('#engine-hide').on("click", function (e) {
1870 show_hide_column(jQuery('#engine-hide').is(":checked"));
1871 });
1872 jQuery('#total_size-hide').on("click", function (e) {
1873 show_hide_column(jQuery('#total_size-hide').is(":checked"));
1874 });
1875 jQuery('#data_size-hide').on("click", function (e) {
1876 show_hide_column(jQuery('#data_size-hide').is(":checked"));
1877 });
1878 jQuery('#index_size-hide').on("click", function (e) {
1879 show_hide_column(jQuery('#index_size-hide').is(":checked"));
1880 });
1881 jQuery('#overhead-hide').on("click", function (e) {
1882 show_hide_column(jQuery('#overhead-hide').is(":checked"));
1883 });
1884 jQuery('#table_collation-hide').on("click", function (e) {
1885 show_hide_column(jQuery('#table_collation-hide').is(":checked"));
1886 });
1887 });
1888 </script>
1889 <?php
1890 }
1891
1892 /**
1893 * Overwrite construct_where_clause()
1894 *
1895 * @since 1.0.0
1896 *
1897 * @see WPDA_List_Table::construct_where_clause()
1898 */
1899 protected function construct_where_clause() {
1900 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
1901 if ( null === $wpdadb ) {
1902 /* translators: %s = remote database name */
1903 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->schema_name ) ) );
1904 }
1905 global $wpdb;
1906 // Make sure we're selecting only tables that are in the WordPress database.
1907 $where_or_and = ( '' === $this->where ? ' where ' : ' and ' );
1908 $this->where .= $wpdadb->prepare( " {$where_or_and} table_schema = %s ", $wpdadb->dbname );
1909 // Since we are using a view, the default behaviour of the parent will not work for us. We have to
1910 // define our where clause manually.
1911 $cols = array(array(
1912 'column_name' => 'table_name',
1913 'data_type' => 'varchar',
1914 'extra' => '',
1915 'column_type' => 'varchar(64)',
1916 'is_nullable' => 'NO',
1917 'column_default' => null,
1918 ));
1919 $where = WPDA::construct_where_clause(
1920 $this->schema_name,
1921 $this->table_name,
1922 $cols,
1923 $this->search_value
1924 );
1925 if ( '' !== $where ) {
1926 $this->where .= ' and ' . $where;
1927 }
1928 if ( $wpdb->dbname === $this->schema_name || '' === $this->schema_name ) {
1929 $table_access = WPDA::get_option( WPDA::OPTION_BE_TABLE_ACCESS );
1930 } else {
1931 $table_access = get_option( WPDA::BACKEND_OPTIONNAME_DATABASE_ACCESS . $this->schema_name );
1932 if ( false === $table_access ) {
1933 $table_access = 'show';
1934 }
1935 }
1936 if ( 'hide' === $table_access ) {
1937 // No access to WordPress tables: filter WordPress table.
1938 $this->where .= " and table_name not in ('" . implode( "','", $wpdb->tables( 'all', true ) ) . "')";
1939 } elseif ( 'select' === $table_access ) {
1940 if ( $wpdb->dbname === $this->schema_name || '' === $this->schema_name ) {
1941 $option = WPDA::get_option( WPDA::OPTION_BE_TABLE_ACCESS_SELECTED );
1942 } else {
1943 $option = get_option( WPDA::BACKEND_OPTIONNAME_DATABASE_SELECTED . $this->schema_name );
1944 if ( false === $option ) {
1945 $option = '';
1946 }
1947 }
1948 if ( '' !== $option ) {
1949 // Allow only access to selected tables.
1950 $this->where .= " and table_name in ('" . implode( "','", $option ) . "')";
1951 } else {
1952 // No tables selected: no access.
1953 $this->where .= ' and 1=2';
1954 }
1955 }
1956 if ( null != $this->wpda_main_favourites ) {
1957 if ( false === $this->favourites && 'show' === $this->wpda_main_favourites ) {
1958 $where_or_and = ( '' === $this->where ? ' where ' : ' and ' );
1959 $this->where .= " {$where_or_and} 1=2 ";
1960 } elseif ( is_array( $this->favourites ) ) {
1961 if ( 0 < count( $this->favourites ) ) {
1962 $where_or_and = ( '' === $this->where ? ' where ' : ' and ' );
1963 $in_or_not_in = ( 'show' === $this->wpda_main_favourites ? 'in' : 'not in' );
1964 $this->where .= " {$where_or_and} table_name {$in_or_not_in} ('" . implode( "','", $this->favourites ) . "') ";
1965 }
1966 }
1967 }
1968 }
1969
1970 protected function get_order_by() {
1971 $orderby = parent::get_order_by();
1972 if ( '' !== $orderby ) {
1973 return $orderby;
1974 } else {
1975 return ' order by table_name asc ';
1976 }
1977 }
1978
1979 /**
1980 * Overwrite method: add button to design a table
1981 */
1982 protected function add_header_button() {
1983 ?>
1984 <form
1985 method="post"
1986 action="?page=<?php
1987 echo esc_attr( \WP_Data_Access_Admin::PAGE_DESIGNER );
1988 ?>"
1989 style="display: inline-block; vertical-align: baseline;"
1990 >
1991 <div>
1992 <input type="hidden" name="action" value="create_table">
1993 <input type="hidden" name="caller" value="dataexplorer">
1994 <button type="submit"
1995 class="page-title-action wpda_tooltip"
1996 title="Create a new table design"
1997 >
1998 <i class="fas fa-plus-circle wpda_icon_on_button"></i>
1999 <?php
2000 echo esc_attr__( 'Design new table', 'wp-data-access' );
2001 ?>
2002 </button>
2003 </div>
2004 </form>
2005 <form id="wpda_linkto_backup" style="display: none" method="post" action="?page=<?php
2006 echo esc_attr( WP_Data_Access_Admin::PAGE_MAIN );
2007 ?>&page_action=wpda_backup">
2008 <input type="hidden" name="wpdaschema_name" value="<?php
2009 echo esc_attr( $this->schema_name );
2010 ?>">
2011 </form>
2012 <?php
2013 $this->wpda_import->add_button();
2014 }
2015
2016 /**
2017 * Add container to create new database
2018 */
2019 protected function add_db_containers() {
2020 if ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ) {
2021 $rdb = WPDADB::get_remote_database( $this->schema_name );
2022 }
2023 $host = ( isset( $rdb['host'] ) ? $rdb['host'] : '' );
2024 $user = ( isset( $rdb['username'] ) ? $rdb['username'] : '' );
2025 $passwd = ( isset( $rdb['password'] ) ? $rdb['password'] : '' );
2026 $port = ( isset( $rdb['port'] ) ? $rdb['port'] : '' );
2027 $schema = ( isset( $rdb['database'] ) ? $rdb['database'] : '' );
2028 $ssl = ( isset( $rdb['ssl'] ) ? $rdb['ssl'] : '' );
2029 $ssl_key = ( isset( $rdb['ssl_key'] ) ? $rdb['ssl_key'] : '' );
2030 $ssl_cert = ( isset( $rdb['ssl_cert'] ) ? $rdb['ssl_cert'] : '' );
2031 $ssl_ca = ( isset( $rdb['ssl_ca'] ) ? $rdb['ssl_ca'] : '' );
2032 $ssl_path = ( isset( $rdb['ssl_path'] ) ? $rdb['ssl_path'] : '' );
2033 $ssl_cipher = ( isset( $rdb['ssl_cipher'] ) ? $rdb['ssl_cipher'] : '' );
2034 $readonly = '';
2035 ?>
2036 <div id="wpda_db_edit" style="display:none;padding-top:10px;">
2037 <div class="wpda_upload">
2038 <form method="post" action="?page=<?php
2039 echo esc_attr( $this->page );
2040 ?>" onsubmit="return editdb_validate_form();">
2041 <div style="height:10px;"></div>
2042 <strong><?php
2043 echo esc_attr__( 'Edit Remote Database Connection', 'wp-data-access' );
2044 ?></strong>
2045 <div style="height:10px;"></div>
2046 <div>
2047 <label for="edit_remote_database" style="vertical-align:baseline;"
2048 class="database_item_label">Database name:</label>
2049 <input type="text" name="edit_remote_database" id="edit_remote_database" value="<?php
2050 echo esc_attr( $this->schema_name );
2051 ?>" <?php
2052 echo esc_attr( $readonly );
2053 ?>>
2054 <input type="hidden" name="edit_remote_database_old" value="<?php
2055 echo esc_attr( $this->schema_name );
2056 ?>">
2057 <div style="height:10px;"></div>
2058 <label for="edit_remote_host" style="vertical-align:baseline;" class="database_item_label">MySQL host:</label>
2059 <input type="text" name="edit_remote_host" id="edit_remote_host" value="<?php
2060 echo esc_attr( $host );
2061 ?>">
2062 <br/>
2063 <label for="edit_remote_user" style="vertical-align:baseline;" class="database_item_label">MySQL username:</label>
2064 <input type="text" name="edit_remote_user" id="edit_remote_user" value="<?php
2065 echo esc_attr( $user );
2066 ?>">
2067 <br/>
2068 <label for="edit_remote_passwd" style="vertical-align:baseline;" class="database_item_label" >MySQL password:</label>
2069 <input type="password" name="edit_remote_passwd" id="edit_remote_passwd" value="<?php
2070 echo esc_attr( $passwd );
2071 ?>" autocomplete="new-password">
2072 <i class="fas fa-eye" onclick="wpda_toggle_password('edit_remote_passwd', event)" style="cursor:pointer"></i>
2073 <br/>
2074 <label for="edit_remote_port" style="vertical-align:baseline;" class="database_item_label">MySQL port:</label>
2075 <input type="text" name="edit_remote_port" id="edit_remote_port" value="<?php
2076 echo esc_attr( $port );
2077 ?>">
2078 <br/>
2079 <label for="edit_remote_schema" style="vertical-align:baseline;" class="database_item_label">MySQL schema:</label>
2080 <input type="text" name="edit_remote_schema" id="edit_remote_schema" value="<?php
2081 echo esc_attr( $schema );
2082 ?>">
2083
2084 <br/>
2085 <label style="line-height:30px;" for="edit_remote_ssl" style="vertical-align:baseline;" class="database_item_label">SSL:</label>
2086 <input type="checkbox" name="edit_remote_ssl" id="edit_remote_ssl" <?php
2087 echo ( 'on' === $ssl ? 'checked' : 'unchecked' );
2088 ?> onclick="jQuery('#edit_remote_database_block_ssl').toggle()">
2089 <div id="edit_remote_database_block_ssl" <?php
2090 echo ( 'on' === $ssl ? '' : 'style="display:none;"' );
2091 ?>>
2092 <label for="edit_remote_client_key" style="vertical-align:baseline;" class="database_item_label">Client key:</label>
2093 <input type="text" name="edit_remote_client_key" id="edit_remote_client_key" value="<?php
2094 echo esc_attr( $ssl_key );
2095 ?>">
2096 <br/>
2097 <label for="edit_remote_client_certificate" style="vertical-align:baseline;" class="database_item_label">Client certificate:</label>
2098 <input type="text" name="edit_remote_client_certificate" id="edit_remote_client_certificate" value="<?php
2099 echo esc_attr( $ssl_cert );
2100 ?>">
2101 <br/>
2102 <label for="edit_remote_ca_certificate" style="vertical-align:baseline;" class="database_item_label">CA certificate:</label>
2103 <input type="text" name="edit_remote_ca_certificate" id="edit_remote_ca_certificate" value="<?php
2104 echo esc_attr( $ssl_ca );
2105 ?>">
2106 <br/>
2107 <label for="edit_remote_certificate_path" style="vertical-align:baseline;" class="database_item_label">Certificate path:</label>
2108 <input type="text" name="edit_remote_certificate_path" id="edit_remote_certificate_path" value="<?php
2109 echo esc_attr( $ssl_path );
2110 ?>">
2111 <br/>
2112 <label for="edit_remote_specified_cipher" style="vertical-align:baseline;" class="database_item_label">Specified Cipher:</label>
2113 <input type="text" name="edit_remote_specified_cipher" id="edit_remote_specified_cipher" value="<?php
2114 echo esc_attr( $ssl_cipher );
2115 ?>">
2116 </div>
2117
2118 <div style="height:10px;"></div>
2119 <label class="database_item_label"></label>
2120 <input type="button" value="Test" onclick="test_remote_connection('edit_'); return false;"
2121 id="edit_remote_test_button" class="button">
2122 <input type="button" value="Clear" onclick="test_remote_clear('edit_'); return false;"
2123 id="edit_remote_clear_button" class="button" style="display:none;">
2124 <div style="height:10px;"></div>
2125 </div>
2126 <div id="edit_remote_database_block_test" style="display:none;">
2127 <div id="edit_remote_database_block_test_content"
2128 class="remote_database_block_test_content"></div>
2129 <div style="height:10px;"></div>
2130 </div>
2131
2132 <input type="submit" class="button button-primary" value="<?php
2133 echo esc_attr__( 'Save', 'wp-data-access' );
2134 ?>">
2135 <a href="javascript:void(0)"
2136 onclick="jQuery('#wpda_db_edit').hide()"
2137 class="button button-secondary"><i class="fas fa-times-circle wpda_icon_on_button"></i> <?php
2138 echo esc_attr__( 'Cancel', 'wp-data-access' );
2139 ?></a>
2140 <input type="hidden" name="action" value="edit_db">
2141 <?php
2142 wp_nonce_field( 'wpda-edit-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnonceeditdb', false );
2143 ?>
2144 </form>
2145 </div>
2146 </div>
2147
2148 <div id="wpda_db_container" style="display:none;padding-top:10px;">
2149 <div class="wpda_upload">
2150 <form method="post" action="?page=<?php
2151 echo esc_attr( $this->page );
2152 ?>" onsubmit="return createdb_validate_form();">
2153 <div style="height:10px;"></div>
2154
2155 <?php
2156 if ( $this->user_can_create_db ) {
2157 ?>
2158 <select name="database_location" id="database_location">
2159 <option value="local"
2160 selected><?php
2161 echo esc_attr__( 'Create local database', 'wp-data-access' );
2162 ?></option>
2163 <option value="remote"><?php
2164 echo esc_attr__( 'Add remote database connection', 'wp-data-access' );
2165 ?></option>
2166 </select>
2167
2168 <div style="height:10px;"></div>
2169
2170 <div id="local_database_block">
2171 <label for="local_database" style="vertical-align:baseline;"
2172 class="database_item_label">Database name:</label>
2173 <input type="text" name="local_database" id="local_database">
2174
2175 <div style="height:10px;"></div>
2176 </div>
2177 <?php
2178 } else {
2179 ?>
2180 <strong><?php
2181 echo esc_attr__( 'Add remote database connection', 'wp-data-access' );
2182 ?></strong>
2183
2184 <div style="height:10px;"></div>
2185 <?php
2186 }
2187 ?>
2188
2189 <div id="remote_database_block"<?php
2190 echo ( $this->user_can_create_db ? 'style="display:none;"' : '' );
2191 ?>>
2192 <div>
2193 <label for="remote_database" style="vertical-align:baseline;"
2194 class="database_item_label">Database name:</label>
2195 <input type="text" name="remote_database" id="remote_database" value="rdb:">
2196 <div style="height:10px;"></div>
2197 <label for="remote_host" style="vertical-align:baseline;" class="database_item_label">MySQL host:</label>
2198 <input type="text" name="remote_host" id="remote_host">
2199 <br/>
2200 <label for="remote_user" style="vertical-align:baseline;" class="database_item_label">MySQL username:</label>
2201 <input type="text" name="remote_user" id="remote_user">
2202 <br/>
2203 <label for="remote_passwd" style="vertical-align:baseline;" class="database_item_label">MySQL password:</label>
2204 <input type="password" name="remote_passwd" id="remote_passwd" autocomplete="new-password">
2205 <i class="fas fa-eye" onclick="wpda_toggle_password('remote_passwd', event)" style="cursor:pointer"></i>
2206 <br/>
2207 <label for="remote_port" style="vertical-align:baseline;" class="database_item_label">MySQL port:</label>
2208 <input type="text" name="remote_port" id="remote_port" value="3306">
2209 <br/>
2210 <label for="remote_schema" style="vertical-align:baseline;" class="database_item_label">MySQL schema:</label>
2211 <input type="text" name="remote_schema" id="remote_schema">
2212
2213 <br/>
2214 <label style="line-height:30px;" for="remote_ssl" style="vertical-align:baseline;" class="database_item_label">SSL:</label>
2215 <input type="checkbox" name="remote_ssl" id="remote_ssl" unchecked onclick="jQuery('#remote_database_block_ssl').toggle()">
2216 <div id="remote_database_block_ssl" style="display:none;">
2217 <label for="remote_client_key" style="vertical-align:baseline;" class="database_item_label">Client key:</label>
2218 <input type="text" name="remote_client_key" id="remote_client_key">
2219 <br/>
2220 <label for="remote_client_certificate" style="vertical-align:baseline;" class="database_item_label">Client certificate:</label>
2221 <input type="text" name="remote_client_certificate" id="remote_client_certificate">
2222 <br/>
2223 <label for="remote_ca_certificate" style="vertical-align:baseline;" class="database_item_label">CA certificate:</label>
2224 <input type="text" name="remote_ca_certificate" id="remote_ca_certificate">
2225 <br/>
2226 <label for="remote_certificate_path" style="vertical-align:baseline;" class="database_item_label">Certificate path:</label>
2227 <input type="text" name="remote_certificate_path" id="remote_certificate_path">
2228 <br/>
2229 <label for="remote_specified_cipher" style="vertical-align:baseline;" class="database_item_label">Specified Cipher:</label>
2230 <input type="text" name="remote_specified_cipher" id="remote_specified_cipher">
2231 </div>
2232
2233 <div style="height:10px;"></div>
2234 <label class="database_item_label"></label>
2235 <input type="button" value="Test" onclick="test_remote_connection(); return false;"
2236 id="remote_test_button" class="button">
2237 <input type="button" value="Clear" onclick="test_remote_clear(); return false;"
2238 id="remote_clear_button" class="button" style="display:none;">
2239 <div style="height:10px;"></div>
2240 </div>
2241 <div id="remote_database_block_test" style="display:none;">
2242 <div id="remote_database_block_test_content"
2243 class="remote_database_block_test_content"></div>
2244 <div style="height:10px;"></div>
2245 </div>
2246 </div>
2247
2248 <a href="javascript:void(0)"
2249 onclick="jQuery(this).closest('form').submit()"
2250 class="button button-primary"><i class="fas fa-cloud-upload wpda_icon_on_button"></i> <?php
2251 echo esc_attr__( 'Save', 'wp-data-access' );
2252 ?></a>
2253 <a href="javascript:void(0)"
2254 onclick="jQuery('#wpda_db_container').hide()"
2255 class="button button-secondary"><i class="fas fa-times-circle wpda_icon_on_button"></i> <?php
2256 echo esc_attr__( 'Cancel', 'wp-data-access' );
2257 ?></a>
2258 <input type="hidden" name="action" value="create_db">
2259 <?php
2260 wp_nonce_field( 'wpda-create-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnoncecreatedb', false );
2261 ?>
2262 </form>
2263 </div>
2264 </div>
2265
2266 <div style="display:none;">
2267 <form id="wpda_form_drop_db" method="post" action="?page=<?php
2268 echo esc_attr( $this->page );
2269 ?>">
2270 <input type="text" name="database" id="drop_database">
2271 <input type="hidden" name="action" value="drop_db">
2272 <?php
2273 wp_nonce_field( 'wpda-drop-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnoncedropdb', false );
2274 ?>
2275 </form>
2276 </div>
2277
2278 <script type='text/javascript'>
2279 function editdb_validate_form() {
2280 if (jQuery('#edit_remote_database').val()==='' || jQuery('#edit_remote_database').val()==='rdb:') {
2281 alert('Database name must be entered');
2282 return false;
2283 }
2284 if (jQuery('#edit_remote_host').val()==='') {
2285 alert('MySQL host must be entered');
2286 return false;
2287 }
2288 if (jQuery('#edit_remote_user').val()==='') {
2289 alert('MySQL username must be entered');
2290 return false;
2291 }
2292 if (jQuery('#edit_remote_schema').val()==='') {
2293 alert('MySQL schema must be entered');
2294 return false;
2295 }
2296 return true;
2297 }
2298
2299 function createdb_validate_form() {
2300 if (jQuery('#database_location').val() === 'remote') {
2301 if (jQuery('#remote_database').val()==='' || jQuery('#remote_database').val()==='rdb:') {
2302 alert('Database name must be entered');
2303 return false;
2304 }
2305 if (jQuery('#remote_host').val()==='') {
2306 alert('MySQL host must be entered');
2307 return false;
2308 }
2309 if (jQuery('#remote_user').val()==='') {
2310 alert('MySQL username must be entered');
2311 return false;
2312 }
2313 if (jQuery('#remote_schema').val()==='') {
2314 alert('MySQL schema must be entered');
2315 return false;
2316 }
2317 } else {
2318
2319 if (jQuery('#local_database').val()==='') {
2320 alert('Database name must be entered');
2321 return false;
2322 }
2323 }
2324 return true;
2325 }
2326
2327 function test_remote_clear(mode = '') {
2328 jQuery('#' + mode + 'remote_database_block_test_content').html('');
2329 jQuery('#' + mode + 'remote_database_block_test').hide();
2330 jQuery('#' + mode + 'remote_clear_button').hide();
2331 }
2332
2333 function test_remote_connection(mode = '') {
2334 host = jQuery('#' + mode + 'remote_host').val();
2335 user = jQuery('#' + mode + 'remote_user').val();
2336 pass = jQuery('#' + mode + 'remote_passwd').val();
2337 port = jQuery('#' + mode + 'remote_port').val();
2338 dbs = jQuery('#' + mode + 'remote_schema').val();
2339 ssl = jQuery('#' + mode + 'remote_ssl').val();
2340 ssl_key = jQuery('#' + mode + 'remote_client_key').val();
2341 ssl_cert = jQuery('#' + mode + 'remote_client_certificate').val();
2342 ssl_ca = jQuery('#' + mode + 'remote_ca_certificate').val();
2343 ssl_path = jQuery('#' + mode + 'remote_certificate_path').val();
2344 ssl_cipher = jQuery('#' + mode + 'remote_specified_cipher').val();
2345
2346 url = '//' + window.location.host + window.location.pathname +
2347 '?action=wpda_check_remote_database_connection';
2348
2349 jQuery('#' + mode + 'remote_test_button').val('Testing...');
2350
2351 jQuery.ajax({
2352 method: 'POST',
2353 url: url,
2354 data: {
2355 host: host,
2356 user: user,
2357 passwd: pass,
2358 port: port,
2359 schema: dbs,
2360 ssl: ssl,
2361 ssl_key: ssl_key,
2362 ssl_cert: ssl_cert,
2363 ssl_ca: ssl_ca,
2364 ssl_path: ssl_path,
2365 ssl_cipher: ssl_cipher
2366 }
2367 }).done(
2368 function (msg) {
2369 jQuery('#' + mode + 'remote_database_block_test_content').html(msg);
2370 jQuery('#' + mode + 'remote_database_block_test').show();
2371 }
2372 ).fail(
2373 function () {
2374 jQuery('#' + mode + 'remote_database_block_test_content').html('Preparing connection...<br/>Establishing connection...<br/><br/><strong>Remote database connection invalid</strong>');
2375 jQuery('#' + mode + 'remote_database_block_test').show();
2376 }
2377 ).always(
2378 function () {
2379 jQuery('#' + mode + 'remote_test_button').val('Test');
2380 jQuery('#' + mode + 'remote_clear_button').show();
2381 }
2382 );
2383 }
2384
2385 jQuery(function () {
2386 jQuery('#database_location').on('change', function () {
2387 if (jQuery(this).val() === 'remote') {
2388 jQuery('#local_database_block').hide();
2389 jQuery('#remote_database_block').show();
2390 } else {
2391 jQuery('#remote_database_block').hide();
2392 jQuery('#local_database_block').show();
2393 }
2394 });
2395 jQuery('#remote_database, #edit_remote_database').keydown(function(e) {
2396 var field = this;
2397 setTimeout(function () {
2398 if (field.value.indexOf('rdb:') !== 0) {
2399 jQuery(field).val('rdb:');
2400 }
2401 }, 1);
2402 });
2403 });
2404 </script>
2405 <?php
2406 }
2407
2408 /**
2409 * Display the search box
2410 *
2411 * @param string $text The 'submit' button label.
2412 * @param string $input_id ID attribute value for the search input field.
2413 *
2414 * @since 1.6.0
2415 */
2416 public function search_box( $text, $input_id ) {
2417 global $wpdb;
2418 $input_id = $input_id . '-search-input';
2419 $schemas = WPDA_Dictionary_Lists::get_db_schemas();
2420 ?>
2421 <div style="padding-top:10px;padding-bottom:0;">
2422 <span style="font-weight: bold;"><?php
2423 echo esc_attr__( 'Database', 'wp-data-access' );
2424 ?>:</span>
2425 <select id="wpda_main_db_schema_list">
2426 <?php
2427 foreach ( $schemas as $schema ) {
2428 if ( $this->schema_name === $schema['schema_name'] ) {
2429 $selected = ' selected';
2430 } else {
2431 $selected = '';
2432 }
2433 if ( $wpdb->dbname === $schema['schema_name'] ) {
2434 $printed_schema_name = "WordPress database ({$schema['schema_name']})";
2435 } else {
2436 $printed_schema_name = $schema['schema_name'];
2437 }
2438 ?>
2439 <option value="<?php
2440 echo esc_attr( $schema['schema_name'] );
2441 ?>" <?php
2442 echo esc_attr( $selected );
2443 ?>>
2444 <?php
2445 echo esc_attr( $printed_schema_name );
2446 ?>
2447 </option>
2448 <?php
2449 }
2450 ?>
2451 </select>
2452 <a class="dashicons dashicons-edit wpda_tooltip" href="javascript:void(0)"
2453 <?php
2454 if ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ) {
2455 ?>
2456 onclick="jQuery('#wpda_db_edit').show();"
2457 <?php
2458 }
2459 ?>
2460 style="<?php
2461 echo ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ? '' : 'color:grey;cursor:default;' );
2462 ?>vertical-align:middle;"
2463 title="<?php
2464 echo ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ? esc_attr__( 'Edit remote database connection', 'wp-data-access' ) : esc_attr__( 'Not available for local database', 'wp-data-access' ) );
2465 ?>">&nbsp;</a>
2466 <a class="dashicons dashicons-plus-alt wpda_tooltip" href="javascript:void(0)"
2467 onclick="jQuery('#wpda_db_container').show(); jQuery('#local_database').focus();"
2468 style="vertical-align:middle;"
2469 title="<?php
2470 echo esc_attr( $this->user_create_db_hint );
2471 ?>">&nbsp;</a>
2472 <a class="dashicons dashicons-dismiss wpda_tooltip" id="wpda_drop_database" href="javascript:void(0)"
2473 <?php
2474 if ( $this->user_can_drop_db ) {
2475 if ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ) {
2476 $msg = esc_html__( 'Delete remote database connection?\\nDoes not drop the database! Only deletes the remote database connection definition.', 'wp-data-access' );
2477 } else {
2478 $msg = esc_html__( 'Drop selected database?', 'wp-data-access' );
2479 }
2480 ?>
2481 onclick="if (confirm('<?php
2482 // phpcs:disable WordPress.Security.EscapeOutput
2483 echo $msg;
2484 // phpcs:disable WordPress.Security.EscapeOutput
2485 ?>')) { jQuery('#drop_database').val(jQuery('#wpda_main_db_schema_list').val()); jQuery('#wpda_form_drop_db').submit(); }"
2486 <?php
2487 }
2488 ?>
2489 style="<?php
2490 echo ( $this->user_can_drop_db ? 'color:#a00;' : 'color:grey;cursor:default;' );
2491 ?>vertical-align:middle;"
2492 title="<?php
2493 echo esc_attr( $this->user_drop_db_hint );
2494 ?>">&nbsp;</a>
2495 <a class="dashicons dashicons-admin-plugins wpda_tooltip"
2496 href="javascript:void(0)"
2497 onclick="wpda_dbinit_admin( '<?php
2498 echo esc_attr( $this->schema_name );
2499 ?>', '<?php
2500 echo esc_attr( wp_create_nonce( 'wpda_dbinit_admin_' . WPDA::get_current_user_login() ) );
2501 ?>' )"
2502 style="vertical-align:middle;"
2503 title="<?php
2504 echo esc_attr__( "Create function wpda_get_wp_user_id() to access the WordPress user ID from database views", 'wp-data-access' );
2505 ?>">&nbsp;</a>
2506 &nbsp;<span style="font-weight: bold;"><?php
2507 esc_html_e( 'Favourites', 'wp-data-access' );
2508 ?>:</span>
2509 <select id="wpda_main_favourites_list">
2510 <option value="" <?php
2511 echo ( '' === $this->wpda_main_favourites ? 'selected' : '' );
2512 ?>>Show all
2513 </option>
2514 <option value="show" <?php
2515 echo ( 'show' === $this->wpda_main_favourites ? 'selected' : '' );
2516 ?>>Show
2517 favourites only
2518 </option>
2519 <option value="hide" <?php
2520 echo ( 'hide' === $this->wpda_main_favourites ? 'selected' : '' );
2521 ?>>Hide
2522 favourites
2523 </option>
2524 </select>
2525 <?php
2526 if ( !(null === $this->search_value && !$this->has_items()) ) {
2527 ?>
2528 <p class="search-box">
2529 <input type="search" id="<?php
2530 echo esc_attr( $input_id );
2531 ?>"
2532 name="<?php
2533 echo esc_attr( $this->search_item_name );
2534 ?>"
2535 value="<?php
2536 echo esc_attr( $this->search_value );
2537 ?>"/>
2538 <?php
2539 submit_button(
2540 $text,
2541 '',
2542 '',
2543 false,
2544 array(
2545 'id' => 'search-submit',
2546 )
2547 );
2548 ?>
2549 <input type="hidden" name="<?php
2550 echo esc_attr( $this->search_item_name );
2551 ?>_old_value"
2552 value="<?php
2553 echo esc_attr( $this->search_value );
2554 ?>"/>
2555 </p>
2556 <?php
2557 }
2558 ?>
2559 </div>
2560 <script type='text/javascript'>
2561 jQuery(function () {
2562 jQuery("#wpda_main_db_schema_list").on("change", function () {
2563 jQuery("#wpda_main_db_schema").val(jQuery(this).val());
2564 jQuery("#wpda_main_form :input[name='action']").val('-1');
2565 jQuery("#wpda_main_form :input[name='action2']").val('-1');
2566 jQuery("#wpda_main_form").submit();
2567 });
2568 jQuery("#wpda_main_favourites_list").on("change", function () {
2569 jQuery("#wpda_main_favourites").val(jQuery(this).val());
2570 jQuery("#wpda_main_form :input[name='action']").val('-1');
2571 jQuery("#wpda_main_form :input[name='action2']").val('-1');
2572 jQuery("#wpda_main_form").submit();
2573 });
2574 });
2575 </script>
2576 <?php
2577 }
2578
2579 /**
2580 * Get schema name from cookie or list
2581 *
2582 * @return null|string
2583 * @since 1.6.0
2584 */
2585 protected function get_schema_name() {
2586 $cookie_name = $this->page . '_schema_name';
2587 $wpda_main_db_schema = null;
2588 if ( isset( $_REQUEST['wpda_main_db_schema'] ) && '' !== $_REQUEST['wpda_main_db_schema'] ) {
2589 $wpda_main_db_schema = sanitize_text_field( wp_unslash( $_REQUEST['wpda_main_db_schema'] ) );
2590 } elseif ( isset( $_COOKIE[$cookie_name] ) ) {
2591 $wpda_main_db_schema = sanitize_text_field( wp_unslash( $_COOKIE[$cookie_name] ) );
2592 // input var okay.
2593 }
2594 if ( null !== $wpda_main_db_schema ) {
2595 if ( '' !== $wpda_main_db_schema && 'rdb:' !== substr( $wpda_main_db_schema, 0, 4 ) ) {
2596 if ( !WPDA_Dictionary_Exist::schema_exists( $wpda_main_db_schema ) ) {
2597 return WPDA::get_user_default_scheme();
2598 }
2599 }
2600 if ( WPDA::schema_disabled( $wpda_main_db_schema ) ) {
2601 return WPDA::get_user_default_scheme();
2602 }
2603 if ( WPDA::schema_exists( $wpda_main_db_schema ) ) {
2604 return $wpda_main_db_schema;
2605 }
2606 /* translators: %s = remote database name */
2607 echo '<p><strong>' . esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $wpda_main_db_schema ) ) . '</strong></p>';
2608 }
2609 return WPDA::get_user_default_scheme();
2610 }
2611
2612 /**
2613 * Get favourite selection from cookie or list
2614 *
2615 * @return null|string
2616 * @since 1.6.0
2617 */
2618 protected function get_favourites() {
2619 $cookie_name = $this->page . '_favourites';
2620 if ( isset( $_REQUEST['wpda_main_favourites'] ) ) {
2621 return sanitize_text_field( wp_unslash( $_REQUEST['wpda_main_favourites'] ) );
2622 } elseif ( isset( $_COOKIE[$cookie_name] ) ) {
2623 return sanitize_text_field( wp_unslash( $_COOKIE[$cookie_name] ) );
2624 } else {
2625 return null;
2626 }
2627 }
2628
2629 /**
2630 * Add labels to static function to make it available to class WPDA_List _View. Allowing to hide columns in
2631 * Data Explorer main page.
2632 *
2633 * @return array
2634 * @since 2.0.3
2635 */
2636 public static function column_headers_labels() {
2637 return array(
2638 'table_name' => __( 'Name', 'wp-data-access' ),
2639 'icons' => '',
2640 'table_type' => __( 'Type', 'wp-data-access' ),
2641 'create_time' => __( 'Creation Date', 'wp-data-access' ),
2642 'table_rows' => __( '#Rows', 'wp-data-access' ),
2643 'auto_increment' => __( 'Increment', 'wp-data-access' ),
2644 'engine' => __( 'Engine', 'wp-data-access' ),
2645 'total_size' => __( 'Size', 'wp-data-access' ),
2646 'data_size' => __( 'Data Size', 'wp-data-access' ),
2647 'index_size' => __( 'Index Size', 'wp-data-access' ),
2648 'overhead' => __( 'Overhead', 'wp-data-access' ),
2649 'table_collation' => __( 'Collation', 'wp-data-access' ),
2650 );
2651 }
2652
2653 }
2654
2655 // phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing