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

WPDA_Actions.php in WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards 5.5.82, at WPDataAccess/API/WPDA_Actions.php

771 lines 35.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDataAccess\API;
4
5 use PDOException;
6 use WPDataAccess\Connection\WPDADB;
7 use WPDataAccess\Plugin_Table_Models\WPDA_Media_Model;
8 use WPDataAccess\Plugin_Table_Models\WPDA_Table_Settings_Model;
9 use WPDataAccess\Plugin_Table_Models\WPDA_User_Menus_Model;
10 use WPDataAccess\Utilities\WPDA_Mail;
11 use WPDataAccess\WPDA;
12 class WPDA_Actions extends WPDA_API_Core {
13 const WPDA_COPY_TABLE = 'wpda_copy_table';
14
15 protected $file_pointer;
16
17 protected $file_content;
18
19 public function register_rest_routes() {
20 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/rename', array(
21 'methods' => array('POST'),
22 'callback' => array($this, 'action_rename'),
23 'permission_callback' => '__return_true',
24 'args' => array(
25 'dbs' => $this->get_param( 'dbs', __( 'Local database name or remote connection string (does not accept system schemas)', 'wp-data-access' ) ),
26 'from_tbl' => $this->get_param( 'tbl', __( 'Source table name (does not rename WordPress tables)', 'wp-data-access' ) ),
27 'to_tbl' => $this->get_param( 'tbl', __( 'Destination table name (cannot overwrite existing table)', 'wp-data-access' ) ),
28 ),
29 ) );
30 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/copy', array(
31 'methods' => array('POST'),
32 'callback' => array($this, 'action_copy'),
33 'permission_callback' => '__return_true',
34 'args' => array(
35 'from_dbs' => $this->get_param( 'dbs', __( 'Source database name or remote connection string', 'wp-data-access' ) ),
36 'to_dbs' => $this->get_param( 'dbs', __( 'Destination database name or remote connection string', 'wp-data-access' ) ),
37 'from_tbl' => $this->get_param( 'tbl', __( 'Source table name', 'wp-data-access' ) ),
38 'to_tbl' => $this->get_param( 'tbl', __( 'Destination table name', 'wp-data-access' ) ),
39 'copy_data' => array(
40 'required' => true,
41 'type' => 'boolean',
42 'description' => __( 'Copy data from source to destination table', 'wp-data-access' ),
43 'sanitize_callback' => 'sanitize_text_field',
44 'validate_callback' => 'rest_validate_request_arg',
45 ),
46 'copy_key' => $this->get_param( 'copy_key' ),
47 ),
48 ) );
49 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/copy/start', array(
50 'methods' => array('POST'),
51 'callback' => array($this, 'action_copy_start'),
52 'permission_callback' => '__return_true',
53 'args' => array(
54 'copy_key' => $this->get_param( 'copy_key' ),
55 ),
56 ) );
57 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/copy/cancel', array(
58 'methods' => array('POST'),
59 'callback' => array($this, 'action_copy_cancel'),
60 'permission_callback' => '__return_true',
61 'args' => array(
62 'copy_key' => $this->get_param( 'copy_key' ),
63 ),
64 ) );
65 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/truncate', array(
66 'methods' => array('POST'),
67 'callback' => array($this, 'action_truncate'),
68 'permission_callback' => '__return_true',
69 'args' => array(
70 'dbs' => $this->get_param( 'dbs', __( 'Local database name or remote connection string (does not accept system schemas)', 'wp-data-access' ) ),
71 'tbl' => $this->get_param( 'tbl', __( 'Source table name (does not truncate WordPress tables)', 'wp-data-access' ) ),
72 ),
73 ) );
74 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/drop', array(
75 'methods' => array('POST'),
76 'callback' => array($this, 'action_drop'),
77 'permission_callback' => '__return_true',
78 'args' => array(
79 'dbs' => $this->get_param( 'dbs', __( 'Local database name or remote connection string (does not accept system schemas)', 'wp-data-access' ) ),
80 'tbl' => $this->get_param( 'tbl', __( 'Source table name (does not drop WordPress tables)', 'wp-data-access' ) ),
81 'typ' => $this->get_param( 'typ' ),
82 ),
83 ) );
84 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/import', array(
85 'methods' => array('POST'),
86 'callback' => array($this, 'action_import'),
87 'permission_callback' => '__return_true',
88 ) );
89 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/mail', array(
90 'methods' => array('POST'),
91 'callback' => array($this, 'action_mail'),
92 'permission_callback' => '__return_true',
93 'args' => array(
94 'to' => array(
95 'required' => false,
96 'type' => 'string',
97 'description' => __( 'To', 'wp-data-access' ),
98 'sanitize_callback' => 'sanitize_text_field',
99 'validate_callback' => 'rest_validate_request_arg',
100 ),
101 'subject' => array(
102 'required' => false,
103 'type' => 'string',
104 'description' => __( 'Subject', 'wp-data-access' ),
105 'sanitize_callback' => 'sanitize_text_field',
106 'validate_callback' => 'rest_validate_request_arg',
107 ),
108 'message' => array(
109 'required' => false,
110 'type' => 'string',
111 'description' => __( 'Message', 'wp-data-access' ),
112 'sanitize_callback' => 'sanitize_text_field',
113 'validate_callback' => 'rest_validate_request_arg',
114 ),
115 ),
116 ) );
117 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/get_row_count', array(
118 'methods' => array('POST'),
119 'callback' => array($this, 'action_get_row_count'),
120 'permission_callback' => '__return_true',
121 'args' => array(
122 'dbs' => $this->get_param( 'dbs', __( 'Local database name or remote connection string', 'wp-data-access' ) ),
123 'tbl' => $this->get_param( 'tbl', __( 'Table name', 'wp-data-access' ) ),
124 ),
125 ) );
126 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/update_row_count', array(
127 'methods' => array('POST'),
128 'callback' => array($this, 'action_update_row_count'),
129 'permission_callback' => '__return_true',
130 'args' => array(
131 'dbs' => $this->get_param( 'dbs', __( 'Local database name or remote connection string', 'wp-data-access' ) ),
132 'tbl' => $this->get_param( 'tbl', __( 'Table name', 'wp-data-access' ) ),
133 'cnt' => $this->get_param( 'row_count', __( 'Hard row count estimation', 'wp-data-access' ) ),
134 ),
135 ) );
136 }
137
138 public function action_update_row_count( $request ) {
139 if ( !$this->current_user_can_access() ) {
140 return $this->unauthorized();
141 }
142 if ( !$this->current_user_token_valid( $request ) ) {
143 return $this->invalid_nonce();
144 }
145 $dbs = $request->get_param( 'dbs' );
146 $tbl = $request->get_param( 'tbl' );
147 $cnt = $request->get_param( 'cnt' );
148 // Get table settings.
149 $settings_db = WPDA_Table_Settings_Model::query( $tbl, $dbs );
150 if ( isset( $settings_db[0]['wpda_table_settings'] ) ) {
151 // Convert string to JSON.
152 $settings = json_decode( $settings_db[0]['wpda_table_settings'], true );
153 if ( isset( $settings['table_settings']['row_count_estimate_value_hard'] ) ) {
154 // Store actual row count as hard estimate.
155 $settings['table_settings']['row_count_estimate_value_hard'] = $cnt;
156 // Save new hard row count.
157 WPDA_Table_Settings_Model::update( $tbl, json_encode( $settings ), $dbs );
158 }
159 return $this->WPDA_Rest_Response( "OK" );
160 } else {
161 WPDA::wpda_log_wp_error( "Error saving hard row count. Please try again later." );
162 }
163 }
164
165 public function action_get_row_count( $request ) {
166 if ( !$this->current_user_can_access() ) {
167 return $this->unauthorized();
168 }
169 if ( !$this->current_user_token_valid( $request ) ) {
170 return $this->invalid_nonce();
171 }
172 $dbs = $request->get_param( 'dbs' );
173 $tbl = $request->get_param( 'tbl' );
174 $count = $this->get_row_count( $dbs, $tbl );
175 return $this->WPDA_Rest_Response( $count );
176 }
177
178 public function action_set_row_count( $request ) {
179 if ( !$this->current_user_can_access() ) {
180 return $this->unauthorized();
181 }
182 if ( !$this->current_user_token_valid( $request ) ) {
183 return $this->invalid_nonce();
184 }
185 $dbs = $request->get_param( 'dbs' );
186 $tbl = $request->get_param( 'tbl' );
187 $count = $this->get_row_count( $dbs, $tbl );
188 return $this->WPDA_Rest_Response( $count );
189 }
190
191 public static function get_row_count( $dbs, $tbl ) {
192 $wpdadb = WPDADB::get_db_connection( $dbs );
193 if ( null === $wpdadb ) {
194 /* translators: %s = database name */
195 return sprintf( __( 'Database %s not available', 'wp-data-access' ), esc_attr( $dbs ) );
196 }
197 $suppress_errors = $wpdadb->suppress_errors;
198 $wpdadb->suppress_errors = true;
199 $count = $wpdadb->get_results( $wpdadb->prepare( 'select count(*) from `%1s`', array($tbl) ), 'ARRAY_N' );
200 $wpdadb->suppress_errors = $suppress_errors;
201 if ( $wpdadb->last_error !== '' ) {
202 WPDA::wpda_log_wp_error( $wpdadb->last_error );
203 }
204 return ( isset( $count[0][0] ) ? $count[0][0] : false );
205 }
206
207 public function action_mail( $request ) {
208 if ( !$this->current_user_can_access() ) {
209 return $this->unauthorized();
210 }
211 if ( !$this->current_user_token_valid( $request ) ) {
212 return $this->invalid_nonce();
213 }
214 $to = $request['to'];
215 $subject = $request['subject'];
216 $message = $request['message'];
217 return WPDA_Mail::send( $to, $subject, $message );
218 }
219
220 public function action_import( $request ) {
221 if ( !$this->current_user_can_access() ) {
222 return $this->unauthorized();
223 }
224 if ( !$this->current_user_token_valid( $request ) ) {
225 return $this->invalid_nonce();
226 }
227 $dbs = $this->sanitize_db_identifier( $request->get_param( 'dbs' ) );
228 $files = $request->get_file_params();
229 $response = array();
230 $errors = false;
231 if ( 0 === count( $files ) || '' === trim( $dbs ) ) {
232 return $this->bad_request();
233 } else {
234 foreach ( $files as $file ) {
235 $temp_file_name = sanitize_text_field( $file['tmp_name'] );
236 // For Windows: do NOT unslash!
237 $temp_file_type = sanitize_text_field( wp_unslash( $file['type'] ) );
238 $orig_file_name = sanitize_text_field( wp_unslash( $file['name'] ) );
239 if ( 0 === $file['error'] && is_uploaded_file( $temp_file_name ) ) {
240 if ( 'application/zip' === $temp_file_type || 'application/x-zip' === $temp_file_type || 'application/x-zip-compressed' === $temp_file_type ) {
241 // Process ZIP file.
242 if ( class_exists( '\\ZipArchive' ) ) {
243 $zip = new \ZipArchive();
244 if ( $zip->open( $temp_file_name ) ) {
245 for ($i = 0; $i < $zip->numFiles; $i++) {
246 $this->file_pointer = $zip->getStream( $zip->getNameIndex( $i ) );
247 $status = $this->import( $zip->getNameIndex( $i ), $dbs );
248 if ( isset( $status['status'], $status['msg'] ) ) {
249 $errors = $errors || 'error' === $status['status'];
250 $response[] = array(
251 $zip->getNameIndex( $i ) => array(
252 'status' => $status['status'],
253 'msg' => $status['msg'],
254 'errors' => $status['errors'],
255 ),
256 );
257 }
258 }
259 } else {
260 // Error reading ZIP file.
261 $errors = true;
262 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
263 $response[] = array(
264 $orig_file_name => array(
265 'status' => 'error',
266 'msg' => sprintf( __( 'Import failed [error reading ZIP file `%s`]', 'wp-data-access' ), $orig_file_name ),
267 ),
268 );
269 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
270 }
271 } else {
272 // ZipArchive not installed.
273 $errors = true;
274 $response[] = array(
275 $orig_file_name => array(
276 'status' => 'error',
277 'msg' => __( 'Import failed - ZipArchive not installed', 'wp-data-access' ),
278 ),
279 );
280 }
281 } else {
282 // Process plain file.
283 // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen
284 $this->file_pointer = fopen( $temp_file_name, 'rb' );
285 // phpcs:enable WordPress.WP.AlternativeFunctions.file_system_operations_fopen
286 $status = $this->import( $orig_file_name, $dbs );
287 if ( isset( $status['status'], $status['msg'] ) ) {
288 $errors = $errors || 'error' === $status['status'];
289 $response[] = array(
290 $orig_file_name => array(
291 'status' => $status['status'],
292 'msg' => $status['msg'],
293 'errors' => $status['errors'],
294 ),
295 );
296 }
297 }
298 }
299 }
300 }
301 if ( $errors ) {
302 $msg = __( 'File(s) imported with errors', 'wp-data-access' );
303 } else {
304 $msg = __( 'File(s) successfully imported', 'wp-data-access' );
305 }
306 return $this->WPDA_Rest_Response( $msg, null, array(
307 'imported' => $response,
308 ) );
309 }
310
311 public function action_drop( $request ) {
312 if ( !$this->current_user_can_access() ) {
313 return $this->unauthorized();
314 }
315 if ( !$this->current_user_token_valid( $request ) ) {
316 return $this->invalid_nonce();
317 }
318 $dbs = $request->get_param( 'dbs' );
319 $tbl = $request->get_param( 'tbl' );
320 $typ = $request->get_param( 'typ' );
321 if ( '' === $dbs || '' === $tbl ) {
322 return $this->bad_request();
323 }
324 global $wpdb;
325 if ( $wpdb->dbname === $dbs && in_array( $tbl, $wpdb->tables() ) ) {
326 return $this->unauthorized();
327 }
328 $msg = $this->drop( $dbs, $tbl, $typ );
329 if ( '' === $msg ) {
330 if ( 1 === $typ ) {
331 return $this->WPDA_Rest_Response( __( 'View successfully dropped', 'wp-data-access' ) );
332 } else {
333 return $this->WPDA_Rest_Response( __( 'Table successfully dropped', 'wp-data-access' ) );
334 }
335 } else {
336 return new \WP_Error('error', $msg, array(
337 'status' => 403,
338 ));
339 }
340 }
341
342 public function action_truncate( $request ) {
343 if ( !$this->current_user_can_access() ) {
344 return $this->unauthorized();
345 }
346 if ( !$this->current_user_token_valid( $request ) ) {
347 return $this->invalid_nonce();
348 }
349 $dbs = $request->get_param( 'dbs' );
350 $tbl = $request->get_param( 'tbl' );
351 if ( '' === $dbs || '' === $tbl ) {
352 return $this->bad_request();
353 }
354 global $wpdb;
355 if ( $wpdb->dbname === $dbs && in_array( $tbl, $wpdb->tables() ) ) {
356 return $this->unauthorized();
357 }
358 $msg = $this->truncate( $dbs, $tbl );
359 if ( '' === $msg ) {
360 return $this->WPDA_Rest_Response( __( 'Table successfully truncated', 'wp-data-access' ) );
361 } else {
362 return new \WP_Error('error', $msg, array(
363 'status' => 403,
364 ));
365 }
366 }
367
368 public function action_copy( $request ) {
369 if ( !$this->current_user_can_access() ) {
370 return $this->unauthorized();
371 }
372 if ( !$this->current_user_token_valid( $request ) ) {
373 return $this->invalid_nonce();
374 }
375 $from_dbs = $request->get_param( 'from_dbs' );
376 $to_dbs = $request->get_param( 'to_dbs' );
377 $from_tbl = $request->get_param( 'from_tbl' );
378 $to_tbl = $request->get_param( 'to_tbl' );
379 $copy_data = $request->get_param( 'copy_data' );
380 $copy_key = $request->get_param( 'copy_key' );
381 if ( '' === $from_dbs || '' === $to_dbs || '' === $from_tbl || '' === $to_tbl ) {
382 return $this->bad_request();
383 }
384 $msg = $this->copy(
385 $from_dbs,
386 $to_dbs,
387 $from_tbl,
388 $to_tbl,
389 $copy_data,
390 $copy_key
391 );
392 if ( '' === $msg ) {
393 return $this->WPDA_Rest_Response( __( 'Table successfully copied', 'wp-data-access' ) );
394 } else {
395 return new \WP_Error('error', $msg, array(
396 'status' => 403,
397 ));
398 }
399 }
400
401 public function action_copy_start( $request ) {
402 if ( !$this->current_user_can_access() ) {
403 return $this->unauthorized();
404 }
405 if ( !$this->current_user_token_valid( $request ) ) {
406 return $this->invalid_nonce();
407 }
408 if ( !WPDA::current_user_is_admin() ) {
409 return $this->unauthorized();
410 }
411 $copy_key = $request->get_param( 'copy_key' );
412 if ( '' === $copy_key ) {
413 return $this->bad_request();
414 }
415 $this->copy_start( $copy_key );
416 return $this->WPDA_Rest_Response( 'ok', self::copy_in_progress() );
417 }
418
419 public function action_copy_cancel( $request ) {
420 if ( !$this->current_user_can_access() ) {
421 return $this->unauthorized();
422 }
423 if ( !$this->current_user_token_valid( $request ) ) {
424 return $this->invalid_nonce();
425 }
426 if ( !WPDA::current_user_is_admin() ) {
427 return $this->unauthorized();
428 }
429 $copy_key = $request->get_param( 'copy_key' );
430 if ( '' === $copy_key ) {
431 return $this->bad_request();
432 }
433 $this->copy_stop( $copy_key );
434 return $this->WPDA_Rest_Response( 'ok', self::copy_in_progress() );
435 }
436
437 public function action_rename( $request ) {
438 if ( !$this->current_user_can_access() ) {
439 return $this->unauthorized();
440 }
441 if ( !$this->current_user_token_valid( $request ) ) {
442 return $this->invalid_nonce();
443 }
444 $dbs = $request->get_param( 'dbs' );
445 $from_tbl = $request->get_param( 'from_tbl' );
446 $to_tbl = $request->get_param( 'to_tbl' );
447 $typ = $request->get_param( 'typ' );
448 if ( '' === $dbs || '' === $from_tbl || '' === $to_tbl ) {
449 return $this->bad_request();
450 }
451 if ( 'information_schema' === $dbs || 'mysql' === $dbs || 'performance_schema' === $dbs || 'sys' === $dbs || '' === $dbs ) {
452 return $this->unauthorized();
453 }
454 global $wpdb;
455 if ( $wpdb->dbname === $dbs && in_array( $from_tbl, $wpdb->tables() ) ) {
456 return $this->unauthorized();
457 }
458 $msg = $this->rename( $dbs, $from_tbl, $to_tbl );
459 if ( '' === $msg ) {
460 if ( 1 === $typ ) {
461 return $this->WPDA_Rest_Response( __( 'View successfully renamed', 'wp-data-access' ) );
462 } else {
463 return $this->WPDA_Rest_Response( __( 'Table successfully renamed', 'wp-data-access' ) );
464 }
465 } else {
466 return new \WP_Error('error', $msg, array(
467 'status' => 403,
468 ));
469 }
470 }
471
472 private function rename( $dbs, $from_tbl, $to_tbl ) {
473 // All values have already been validated and sanitized in the rest route registration.
474 if ( !WPDA::current_user_is_admin() ) {
475 return 'Unauthorized';
476 }
477 $wpdadb = WPDADB::get_db_connection( $dbs );
478 if ( null === $wpdadb ) {
479 /* translators: %s = database name */
480 return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) );
481 }
482 $suppress_errors = $wpdadb->suppress_errors;
483 $wpdadb->suppress_errors = true;
484 $wpdadb->query( $wpdadb->prepare( 'rename table `%1s` to `%1s`', array($from_tbl, $to_tbl) ) );
485 $wpdadb->suppress_errors = $suppress_errors;
486 return $wpdadb->last_error;
487 }
488
489 private function copy(
490 $from_dbs,
491 $to_dbs,
492 $from_tbl,
493 $to_tbl,
494 $copy_data,
495 $copy_key
496 ) {
497 // All values have already been validated and sanitized in the rest route registration.
498 if ( !WPDA::current_user_is_admin() ) {
499 return 'Unauthorized';
500 }
501 $wpdadb_from = WPDADB::get_db_connection( $from_dbs );
502 if ( null === $wpdadb_from ) {
503 /* translators: %s = database name */
504 return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $from_dbs ) );
505 }
506 $wpdadb_to = WPDADB::get_db_connection( $to_dbs );
507 if ( null === $wpdadb_to ) {
508 /* translators: %s = database name */
509 return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $to_dbs ) );
510 }
511 $suppress_errors_from = $wpdadb_from->suppress_errors;
512 $wpdadb_from->suppress_errors = true;
513 $suppress_errors_to = $wpdadb_to->suppress_errors;
514 $wpdadb_to->suppress_errors = true;
515 // Get create table statement.
516 // NO_TABLE_OPTIONS is deprecated in V8
517 // $wpdadb_from->query( "SET sql_mode = 'NO_TABLE_OPTIONS'" );
518 $sql_cmd = $wpdadb_from->get_results( $wpdadb_from->prepare( 'show create table `%1s`', array($from_tbl) ), 'ARRAY_A' );
519 // Check for errors.
520 if ( '' !== $wpdadb_from->last_error ) {
521 $wpdadb_from->suppress_errors = $suppress_errors_from;
522 $wpdadb_to->suppress_errors = $suppress_errors_to;
523 return $wpdadb_from->last_error;
524 }
525 if ( !isset( $sql_cmd[0]['Create Table'] ) ) {
526 $wpdadb_from->suppress_errors = $suppress_errors_from;
527 $wpdadb_to->suppress_errors = $suppress_errors_to;
528 return 'Create command table failed';
529 }
530 // Update destination table name if applicable.
531 $create_table_statement = $sql_cmd[0]['Create Table'];
532 if ( $from_tbl !== $to_tbl ) {
533 // Modify create table statement
534 $pos = strpos( $create_table_statement, $from_tbl );
535 if ( $pos !== false ) {
536 $create_table_statement = substr_replace(
537 $create_table_statement,
538 $to_tbl,
539 $pos,
540 strlen( $from_tbl )
541 );
542 }
543 }
544 // Create new table.
545 $wpdadb_to->query( $create_table_statement );
546 // Check for errors.
547 if ( '' !== $wpdadb_to->last_error ) {
548 $wpdadb_from->suppress_errors = $suppress_errors_from;
549 $wpdadb_to->suppress_errors = $suppress_errors_to;
550 return $wpdadb_to->last_error;
551 }
552 if ( '1' === $copy_data ) {
553 // Copy data from source to destination table.
554 // Prevent time out.
555 // phpcs:disable Squiz.PHP.DiscouragedFunctions.Discouraged
556 set_time_limit( 0 );
557 // phpcs:enable Squiz.PHP.DiscouragedFunctions.Discouraged
558 // Buffer rows to prevent exhausting memory.
559 $buffer_size = 1000;
560 // Default buffer
561 $settings_db = WPDA_Table_Settings_Model::query( $from_tbl, $from_dbs );
562 if ( isset( $settings_db[0]['wpda_table_settings'] ) ) {
563 // Convert string to JSON.
564 $settings = json_decode( $settings_db[0]['wpda_table_settings'], true );
565 if ( isset( $settings['table_settings']['query_buffer_size'] ) && is_numeric( $settings['table_settings']['query_buffer_size'] ) ) {
566 $buffer_size = intval( $settings['table_settings']['query_buffer_size'] );
567 }
568 }
569 $index = 0;
570 $loop_done = false;
571 $wpdadb_from->save_queries = false;
572 $wpdadb_to->save_queries = false;
573 while ( !$loop_done ) {
574 // Get rows.
575 $rows = $wpdadb_from->get_results( $wpdadb_from->prepare( 'select * from `%1s` limit %d offset %d', array($from_tbl, $buffer_size, $index * $buffer_size) ), 'ARRAY_A' );
576 // Process rows.
577 foreach ( $rows as $row ) {
578 $wpdadb_to->insert( $to_tbl, $row );
579 }
580 // Cleanup = IMPORTANT!
581 $wpdadb_from->flush();
582 $wpdadb_to->flush();
583 $wpdadb_to->queries = array();
584 $wpdadb_to->last_query = '';
585 $wpdadb_to->last_error = '';
586 $wpdadb_to->result = null;
587 $wpdadb_to->num_rows = 0;
588 $wpdadb_to->rows_affected = 0;
589 wp_cache_flush();
590 if ( function_exists( 'gc_collect_cycles' ) ) {
591 gc_collect_cycles();
592 }
593 if ( 100 > count( $rows ) ) {
594 // No more rows to process.
595 $loop_done = true;
596 }
597 unset($rows);
598 $index++;
599 // Check if job was cancelled
600 $in_progress = self::copy_in_progress();
601 if ( !in_array( $copy_key, $in_progress ) ) {
602 $loop_done = true;
603 }
604 }
605 }
606 $wpdadb_from->suppress_errors = $suppress_errors_from;
607 $wpdadb_to->suppress_errors = $suppress_errors_to;
608 $this->copy_stop( $copy_key );
609 if ( '' !== $wpdadb_from->last_error ) {
610 return $wpdadb_from->last_error;
611 }
612 if ( '' !== $wpdadb_to->last_error ) {
613 return $wpdadb_to->last_error;
614 }
615 return '';
616 }
617
618 private function copy_start( $copy_key ) {
619 $in_progress = self::copy_in_progress();
620 $in_progress[] = $copy_key;
621 update_user_meta( WPDA::get_current_user_id(), self::WPDA_COPY_TABLE, $in_progress );
622 }
623
624 private function copy_stop( $copy_key ) {
625 $in_progress = self::copy_in_progress();
626 if ( ($array_key = array_search( $copy_key, $in_progress )) !== false ) {
627 unset($in_progress[$array_key]);
628 $in_progress = array_values( $in_progress );
629 // Reset indexes to start at 0
630 }
631 update_user_meta( WPDA::get_current_user_id(), self::WPDA_COPY_TABLE, $in_progress );
632 }
633
634 public static function copy_in_progress() {
635 $in_progress = get_user_meta( WPDA::get_current_user_id(), self::WPDA_COPY_TABLE, true );
636 if ( $in_progress === false || $in_progress === '' ) {
637 return array();
638 }
639 return maybe_unserialize( $in_progress );
640 }
641
642 private function truncate( $dbs, $tbl ) {
643 // All values have already been validated and sanitized in the rest route registration.
644 if ( !WPDA::current_user_is_admin() ) {
645 return 'Unauthorized';
646 }
647 $wpdadb = WPDADB::get_db_connection( $dbs );
648 if ( null === $wpdadb ) {
649 /* translators: %s = database name */
650 return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) );
651 }
652 $suppress_errors = $wpdadb->suppress_errors;
653 $wpdadb->suppress_errors = true;
654 $wpdadb->query( $wpdadb->prepare( 'truncate table `%1s`', array($tbl) ) );
655 $wpdadb->suppress_errors = $suppress_errors;
656 return $wpdadb->last_error;
657 }
658
659 private function drop( $dbs, $tbl, $typ ) {
660 // All values have already been validated and sanitized in the rest route registration.
661 if ( !WPDA::current_user_is_admin() ) {
662 return 'Unauthorized';
663 }
664 $wpdadb = WPDADB::get_db_connection( $dbs );
665 if ( null === $wpdadb ) {
666 /* translators: %s = database name */
667 return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) );
668 }
669 $suppress_errors = $wpdadb->suppress_errors;
670 $wpdadb->suppress_errors = true;
671 if ( 1 === $typ ) {
672 $wpdadb->query( $wpdadb->prepare( 'drop view `%1s`', array($tbl) ) );
673 } else {
674 $wpdadb->query( $wpdadb->prepare( 'drop table `%1s`', array($tbl) ) );
675 }
676 if ( '' !== $wpdadb->last_error ) {
677 return $wpdadb->last_error;
678 }
679 $this->post_drop_table( $dbs, $tbl );
680 $wpdadb->suppress_errors = $suppress_errors;
681 return $wpdadb->last_error;
682 }
683
684 private function post_drop_table( $dbs, $tbl ) {
685 global $wpdb;
686 $suppress = $wpdb->suppress_errors( true );
687 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQLPlaceholders, WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder -- plugin table
688 // Table settings...
689 $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() ), $dbs, $tbl) ) );
690 // WordPress media library columns...
691 $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() ), $dbs, $tbl) ) );
692 // Data menus...
693 $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() ), $dbs, $tbl) ) );
694 $wpdb->suppress_errors( $suppress );
695 }
696
697 private function import( $file_name, $dbs ) {
698 if ( !WPDA::current_user_is_admin() ) {
699 return array(
700 'status' => 'error',
701 'msg' => 'Unauthorized',
702 );
703 }
704 $errors = array();
705 global $wpdb;
706 $wpdadb = WPDADB::get_db_connection( $dbs );
707 if ( null === $wpdadb ) {
708 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
709 return array(
710 'status' => 'error',
711 'msg' => sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) ),
712 );
713 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
714 }
715 $suppress = $wpdadb->suppress_errors( true );
716 if ( false !== $this->file_pointer ) {
717 while ( !feof( $this->file_pointer ) ) {
718 // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fread
719 $this->file_content .= fread( $this->file_pointer, 4096 );
720 // phpcs:enable WordPress.WP.AlternativeFunctions.file_system_operations_fread
721 // Replace WP prefix and WPDA prefix.
722 $this->file_content = str_replace( '{wp_schema}', $wpdb->dbname, $this->file_content );
723 $this->file_content = str_replace( '{wp_prefix}', $wpdb->prefix, $this->file_content );
724 $this->file_content = str_replace( '{wpda_prefix}', 'wpda', $this->file_content );
725 // for backward compatibility
726 // Find and process SQL statements.
727 $sql_end_unix = strpos( $this->file_content, ";\n" );
728 $sql_end_windows = strpos( $this->file_content, ";\r\n" );
729 while ( false !== $sql_end_unix || false !== $sql_end_windows ) {
730 if ( false === $sql_end_unix ) {
731 $sql_end = $sql_end_windows;
732 } elseif ( false === $sql_end_windows ) {
733 $sql_end = $sql_end_unix;
734 } else {
735 $sql_end = min( $sql_end_unix, $sql_end_windows );
736 }
737 $sql = rtrim( substr( $this->file_content, 0, $sql_end ) );
738 $this->file_content = substr( $this->file_content, strpos( $this->file_content, $sql ) + strlen( $sql ) + 1 );
739 if ( false === $wpdadb->query( $sql ) ) {
740 if ( '' !== $wpdadb->last_error ) {
741 $errors[] = $wpdadb->last_error;
742 }
743 }
744 // Find next SQL statement.
745 $sql_end_unix = strpos( $this->file_content, ";\n" );
746 $sql_end_windows = strpos( $this->file_content, ";\r\n" );
747 }
748 }
749 }
750 $wpdadb->suppress_errors( $suppress );
751 // Process file content.
752 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
753 if ( 0 < count( $errors ) ) {
754 return array(
755 'status' => 'error',
756 'msg' => sprintf( __( 'Import `%s` failed [check import file]', 'wp-data-access' ), $file_name ),
757 'errors' => $errors,
758 );
759 } else {
760 // Import succeeded.
761 return array(
762 'status' => 'ok',
763 'msg' => sprintf( __( 'Import `%s` completed succesfully', 'wp-data-access' ), $file_name ),
764 'errors' => $errors,
765 );
766 }
767 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
768 }
769
770 }
771