PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.84
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.84
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
← All changes | WPDataAccess/API/WPDA_Actions.php +742 -548 5.5.35.5.84 View file →
@@ -1,576 +1,770 @@
1 1 <?php
2 2
3 -namespace WPDataAccess\API {
3 +namespace WPDataAccess\API;
4 4
5 - use WPDataAccess\Connection\WPDADB;
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';
6 14
7 - class WPDA_Actions extends WPDA_API_Core {
15 + protected $file_pointer;
8 16
9 - public function register_rest_routes() {
17 + protected $file_content;
10 18
11 - register_rest_route(
12 - WPDA_API::WPDA_NAMESPACE,
13 - 'action/rename',
14 - array(
15 - 'methods' => array( 'POST' ),
16 - 'callback' => array( $this, 'action_rename' ),
17 - 'permission_callback' => '__return_true',
18 - 'args' => array(
19 - 'dbs' => array(
20 - 'required' => true,
21 - 'type' => 'string',
22 - 'description' => __( 'Local database name or remote connection string (does not accept system schemas)', 'wp-data-access' ),
23 - 'sanitize_callback' => function ( $param ) {
24 - return $this->sanitize_db_identifier( $param );
25 - },
26 - 'validate_callback' => function ( $param ) {
27 - return $this->validate_db_identifier( $param );
28 - },
29 - ),
30 - 'from_tbl' => array(
31 - 'required' => true,
32 - 'type' => 'string',
33 - 'description' => __( 'Source table name (does not rename WordPress tables)', 'wp-data-access' ),
34 - 'sanitize_callback' => function ( $param ) {
35 - return $this->sanitize_db_identifier( $param );
36 - },
37 - 'validate_callback' => function ( $param ) {
38 - return $this->validate_db_identifier( $param );
39 - },
40 - ),
41 - 'to_tbl' => array(
42 - 'required' => true,
43 - 'type' => 'string',
44 - 'description' => __( 'Destination table name (cannot overwrite existing table)', 'wp-data-access' ),
45 - 'sanitize_callback' => function ( $param ) {
46 - return $this->sanitize_db_identifier( $param );
47 - },
48 - 'validate_callback' => function ( $param ) {
49 - return $this->validate_db_identifier( $param );
50 - },
51 - ),
52 - ),
53 - )
54 - );
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 + }
55 137
56 - register_rest_route(
57 - WPDA_API::WPDA_NAMESPACE,
58 - 'action/copy',
59 - array(
60 - 'methods' => array( 'POST' ),
61 - 'callback' => array( $this, 'action_copy' ),
62 - 'permission_callback' => '__return_true',
63 - 'args' => array(
64 - 'from_dbs' => array(
65 - 'required' => true,
66 - 'type' => 'string',
67 - 'description' => __( 'Source database name or remote connection string', 'wp-data-access' ),
68 - 'sanitize_callback' => function ( $param ) {
69 - return $this->sanitize_db_identifier( $param );
70 - },
71 - 'validate_callback' => function ( $param ) {
72 - return $this->validate_db_identifier( $param );
73 - },
74 - ),
75 - 'to_dbs' => array(
76 - 'required' => true,
77 - 'type' => 'string',
78 - 'description' => __( 'Destination database name or remote connection string', 'wp-data-access' ),
79 - 'sanitize_callback' => function ( $param ) {
80 - return $this->sanitize_db_identifier( $param );
81 - },
82 - 'validate_callback' => function ( $param ) {
83 - return $this->validate_db_identifier( $param );
84 - },
85 - ),
86 - 'from_tbl' => array(
87 - 'required' => true,
88 - 'type' => 'string',
89 - 'description' => __( 'Source table name', 'wp-data-access' ),
90 - 'sanitize_callback' => function ( $param ) {
91 - return $this->sanitize_db_identifier( $param );
92 - },
93 - 'validate_callback' => function ( $param ) {
94 - return $this->validate_db_identifier( $param );
95 - },
96 - ),
97 - 'to_tbl' => array(
98 - 'required' => true,
99 - 'type' => 'string',
100 - 'description' => __( 'Destination table name', 'wp-data-access' ),
101 - 'sanitize_callback' => function ( $param ) {
102 - return $this->sanitize_db_identifier( $param );
103 - },
104 - 'validate_callback' => function ( $param ) {
105 - return $this->validate_db_identifier( $param );
106 - },
107 - ),
108 - 'copy_data' => array(
109 - 'required' => true,
110 - 'type' => 'boolean',
111 - 'description' => __( 'Copy data from source to destination table', 'wp-data-access' ),
112 - 'sanitize_callback' => 'sanitize_text_field',
113 - 'validate_callback' => 'rest_validate_request_arg',
114 - ),
115 - ),
116 - )
117 - );
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 + }
118 164
119 - register_rest_route(
120 - WPDA_API::WPDA_NAMESPACE,
121 - 'action/truncate',
122 - array(
123 - 'methods' => array( 'POST' ),
124 - 'callback' => array( $this, 'action_truncate' ),
125 - 'permission_callback' => '__return_true',
126 - 'args' => array(
127 - 'dbs' => array(
128 - 'required' => true,
129 - 'type' => 'string',
130 - 'description' => __( 'Local database name or remote connection string (does not accept system schemas)', 'wp-data-access' ),
131 - 'sanitize_callback' => function ( $param ) {
132 - return $this->sanitize_db_identifier( $param );
133 - },
134 - 'validate_callback' => function ( $param ) {
135 - return $this->validate_db_identifier( $param );
136 - },
137 - ),
138 - 'tbl' => array(
139 - 'required' => true,
140 - 'type' => 'string',
141 - 'description' => __( 'Source table name (does not truncate WordPress tables)', 'wp-data-access' ),
142 - 'sanitize_callback' => function ( $param ) {
143 - return $this->sanitize_db_identifier( $param );
144 - },
145 - 'validate_callback' => function ( $param ) {
146 - return $this->validate_db_identifier( $param );
147 - },
148 - ),
149 - ),
150 - )
151 - );
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 + }
152 177
153 - register_rest_route(
154 - WPDA_API::WPDA_NAMESPACE,
155 - 'action/drop',
156 - array(
157 - 'methods' => array( 'POST' ),
158 - 'callback' => array( $this, 'action_drop' ),
159 - 'permission_callback' => '__return_true',
160 - 'args' => array(
161 - 'dbs' => array(
162 - 'required' => true,
163 - 'type' => 'string',
164 - 'description' => __( 'Local database name or remote connection string (does not accept system schemas)', 'wp-data-access' ),
165 - 'sanitize_callback' => function ( $param ) {
166 - return $this->sanitize_db_identifier( $param );
167 - },
168 - 'validate_callback' => function ( $param ) {
169 - return $this->validate_db_identifier( $param );
170 - },
171 - ),
172 - 'tbl' => array(
173 - 'required' => true,
174 - 'type' => 'string',
175 - 'description' => __( 'Source table name (does not drop WordPress tables)', 'wp-data-access' ),
176 - 'sanitize_callback' => function ( $param ) {
177 - return $this->sanitize_db_identifier( $param );
178 - },
179 - 'validate_callback' => function ( $param ) {
180 - return $this->validate_db_identifier( $param );
181 - },
182 - ),
183 - ),
184 - )
185 - );
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 + }
186 190
187 - }
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 + }
188 206
189 - public function action_drop( $request ) {
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 + }
190 219
191 - if ( ! $this->current_user_can_access( true ) ) {
192 - return $this->unauthorized();
193 - }
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 + }
194 310
195 - if ( ! $this->current_user_token_valid( $request, true ) ) {
196 - return $this->invalid_nonce();
197 - }
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 + }
198 341
199 - $dbs = $request->get_param( 'dbs' );
200 - $tbl = $request->get_param( 'tbl' );
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 + }
201 367
202 - if (
203 - '' === $dbs ||
204 - '' === $tbl
205 - ) {
206 - return $this->bad_request();
207 - }
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 + }
208 400
209 - global $wpdb;
210 - if (
211 - $wpdb->dbname === $dbs &&
212 - in_array( $tbl, $wpdb->tables() )
213 - ) {
214 - return $this->unauthorized();
215 - }
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 + }
216 418
217 - $msg = $this->drop( $dbs, $tbl );
218 - if ( '' === $msg ) {
219 - return $this->WPDA_Rest_Response( __( 'Table successfully dropped', 'wp-data-access' ) );
220 - } else {
221 - return new \WP_Error( 'error', $msg, array( 'status' => 403 ) );
222 - }
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 + }
223 436
224 - }
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 + }
225 471
226 - public function action_truncate( $request ) {
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 + }
227 488
228 - if ( ! $this->current_user_can_access( true ) ) {
229 - return $this->unauthorized();
230 - }
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 + }
231 617
232 - if ( ! $this->current_user_token_valid( $request, true ) ) {
233 - return $this->invalid_nonce();
234 - }
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 + }
235 623
236 - $dbs = $request->get_param( 'dbs' );
237 - $tbl = $request->get_param( 'tbl' );
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 + }
238 633
239 - if (
240 - '' === $dbs ||
241 - '' === $tbl
242 - ) {
243 - return $this->bad_request();
244 - }
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 + }
245 641
246 - global $wpdb;
247 - if (
248 - $wpdb->dbname === $dbs &&
249 - in_array( $tbl, $wpdb->tables() )
250 - ) {
251 - return $this->unauthorized();
252 - }
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 + }
253 658
254 - $msg = $this->truncate( $dbs, $tbl );
255 - if ( '' === $msg ) {
256 - return $this->WPDA_Rest_Response( __( 'Table successfully truncated', 'wp-data-access' ) );
257 - } else {
258 - return new \WP_Error( 'error', $msg, array( 'status' => 403 ) );
259 - }
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 + }
260 683
261 - }
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 + }
262 696
263 - public function action_copy( $request ) {
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 + }
264 769
265 - if ( ! $this->current_user_can_access( true ) ) {
266 - return $this->unauthorized();
267 - }
268 -
269 - if ( ! $this->current_user_token_valid( $request, true ) ) {
270 - return $this->invalid_nonce();
271 - }
272 -
273 - $from_dbs = $request->get_param( 'from_dbs' );
274 - $to_dbs = $request->get_param( 'to_dbs' );
275 - $from_tbl = $request->get_param( 'from_tbl' );
276 - $to_tbl = $request->get_param( 'to_tbl' );
277 - $copy_data = $request->get_param( 'copy_data' );
278 -
279 - if (
280 - '' === $from_dbs ||
281 - '' === $to_dbs ||
282 - '' === $from_tbl ||
283 - '' === $to_tbl
284 - ) {
285 - return $this->bad_request();
286 - }
287 -
288 - $msg = $this->copy( $from_dbs, $to_dbs, $from_tbl, $to_tbl, $copy_data );
289 - if ( '' === $msg ) {
290 - return $this->WPDA_Rest_Response( __( 'Table successfully copied', 'wp-data-access' ) );
291 - } else {
292 - return new \WP_Error( 'error', $msg, array( 'status' => 403 ) );
293 - }
294 -
295 - }
296 -
297 - public function action_rename( $request ) {
298 -
299 - if ( ! $this->current_user_can_access( true ) ) {
300 - return $this->unauthorized();
301 - }
302 -
303 - if ( ! $this->current_user_token_valid( $request, true ) ) {
304 - return $this->invalid_nonce();
305 - }
306 -
307 - $dbs = $request->get_param( 'dbs' );
308 - $from_tbl = $request->get_param( 'from_tbl' );
309 - $to_tbl = $request->get_param( 'to_tbl' );
310 -
311 - if (
312 - '' === $dbs ||
313 - '' === $from_tbl ||
314 - '' === $to_tbl
315 - ) {
316 - return $this->bad_request();
317 - }
318 -
319 - if (
320 - 'information_schema' === $dbs ||
321 - 'mysql' === $dbs ||
322 - 'performance_schema' === $dbs ||
323 - 'sys' === $dbs ||
324 - '' === $dbs
325 - ) {
326 - return $this->unauthorized();
327 - }
328 -
329 - global $wpdb;
330 - if (
331 - $wpdb->dbname === $dbs &&
332 - in_array( $from_tbl, $wpdb->tables() )
333 - ) {
334 - return $this->unauthorized();
335 - }
336 -
337 - $msg = $this->rename( $dbs, $from_tbl, $to_tbl );
338 - if ( '' === $msg ) {
339 - return $this->WPDA_Rest_Response( __( 'Table successfully renamed', 'wp-data-access' ) );
340 - } else {
341 - return new \WP_Error( 'error', $msg, array( 'status' => 403 ) );
342 - }
343 -
344 - }
345 -
346 - private function rename(
347 - $dbs,
348 - $from_tbl,
349 - $to_tbl
350 - ) {
351 -
352 - // All values have already been validated and sanitized in the rest route registration.
353 -
354 - if ( ! current_user_can( 'manage_options' ) ) {
355 - return 'Unauthorized';
356 - }
357 -
358 - $wpdadb = WPDADB::get_db_connection( $dbs );
359 - if ( null === $wpdadb ) {
360 - return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) );
361 - }
362 -
363 - $suppress_errors = $wpdadb->suppress_errors;
364 - $wpdadb->suppress_errors = true;
365 -
366 - $wpdadb->query(
367 - $wpdadb->prepare(
368 - 'rename table `%1s` to `%1s`',
369 - array(
370 - $from_tbl,
371 - $to_tbl,
372 - )
373 - )
374 - );
375 -
376 - $wpdadb->suppress_errors = $suppress_errors;
377 -
378 - return $wpdadb->last_error;
379 -
380 - }
381 -
382 - private function copy(
383 - $from_dbs,
384 - $to_dbs,
385 - $from_tbl,
386 - $to_tbl,
387 - $copy_data
388 - ) {
389 -
390 - // All values have already been validated and sanitized in the rest route registration.
391 -
392 - if ( ! current_user_can( 'manage_options' ) ) {
393 - return 'Unauthorized';
394 - }
395 -
396 - $wpdadb_from = WPDADB::get_db_connection( $from_dbs );
397 - if ( null === $wpdadb_from ) {
398 - return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $from_dbs ) );
399 - }
400 -
401 - $wpdadb_to = WPDADB::get_db_connection( $to_dbs );
402 - if ( null === $wpdadb_to ) {
403 - return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $to_dbs ) );
404 - }
405 -
406 - $suppress_errors_from = $wpdadb_from->suppress_errors;
407 - $wpdadb_from->suppress_errors = true;
408 -
409 - $suppress_errors_to = $wpdadb_to->suppress_errors;
410 - $wpdadb_to->suppress_errors = true;
411 -
412 - // Get create table statement.
413 - $wpdadb_from->query( "SET sql_mode = 'NO_TABLE_OPTIONS'" );
414 - $sql_cmd = $wpdadb_from->get_results(
415 - $wpdadb_from->prepare(
416 - 'show create table `%1s`',
417 - array(
418 - $from_tbl
419 - )
420 - ),
421 - 'ARRAY_A'
422 - );
423 -
424 - // Check for errors.
425 - if ( '' !== $wpdadb_from->last_error ) {
426 - $wpdadb_from->suppress_errors = $suppress_errors_from;
427 - $wpdadb_to->suppress_errors = $suppress_errors_to;
428 -
429 - return $wpdadb_from->last_error;
430 - }
431 -
432 - if ( ! isset( $sql_cmd[0]['Create Table'] ) ) {
433 - $wpdadb_from->suppress_errors = $suppress_errors_from;
434 - $wpdadb_to->suppress_errors = $suppress_errors_to;
435 -
436 - return 'Create command table failed';
437 - }
438 -
439 - // Update destination table name if applicable.
440 - $create_table_statement = $sql_cmd[0]['Create Table'];
441 - if ( $from_tbl !== $to_tbl ) {
442 - // Modify create table statement
443 - $pos = strpos( $create_table_statement, $from_tbl );
444 - if ($pos !== false) {
445 - $create_table_statement = substr_replace( $create_table_statement, $to_tbl, $pos, strlen( $from_tbl ) );
446 - }
447 - }
448 -
449 - // Create new table.
450 - $wpdadb_to->query( $create_table_statement );
451 -
452 - // Check for errors.
453 - if ( '' !== $wpdadb_to->last_error ) {
454 - $wpdadb_from->suppress_errors = $suppress_errors_from;
455 - $wpdadb_to->suppress_errors = $suppress_errors_to;
456 -
457 - return $wpdadb_to->last_error;
458 - }
459 -
460 - if ( '1' === $copy_data ) {
461 - // Copy data from source to destination table.
462 - set_time_limit( 0 ); // Prevent time out.
463 - // Use a cursor to process all rows and prevent exhausting memory.
464 - // Process 100 rows per batch to prevent exhausting memory.
465 - $buffer_size = 100;
466 - $index = 0;
467 - $loop_done = false;
468 - while ( ! $loop_done ) {
469 - // Get rows.
470 - $rows = $wpdadb_from->get_results(
471 - $wpdadb_from->prepare(
472 - 'select * from `%1s` limit %1s offset %1s',
473 - array(
474 - $from_tbl,
475 - $buffer_size,
476 - $index * $buffer_size
477 - )
478 - ),
479 - 'ARRAY_A'
480 - );
481 -
482 - // Process rows.
483 - foreach ( $rows as $row ) {
484 - $wpdadb_to->insert(
485 - $to_tbl,
486 - $row
487 - );
488 - }
489 -
490 - if ( 100 > count( $rows ) ) {
491 - // No more rows to process.
492 - $loop_done = true;
493 - }
494 -
495 - $index++;
496 - }
497 - }
498 -
499 - $wpdadb_from->suppress_errors = $suppress_errors_from;
500 - $wpdadb_to->suppress_errors = $suppress_errors_to;
501 -
502 - return '';
503 -
504 - }
505 -
506 - private function truncate(
507 - $dbs,
508 - $tbl
509 - ) {
510 -
511 - // All values have already been validated and sanitized in the rest route registration.
512 -
513 - if ( ! current_user_can( 'manage_options' ) ) {
514 - return 'Unauthorized';
515 - }
516 -
517 - $wpdadb = WPDADB::get_db_connection( $dbs );
518 - if ( null === $wpdadb ) {
519 - return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) );
520 - }
521 -
522 - $suppress_errors = $wpdadb->suppress_errors;
523 - $wpdadb->suppress_errors = true;
524 -
525 - $wpdadb->query(
526 - $wpdadb->prepare(
527 - 'truncate table `%1s`',
528 - array(
529 - $tbl,
530 - )
531 - )
532 - );
533 -
534 - $wpdadb->suppress_errors = $suppress_errors;
535 -
536 - return $wpdadb->last_error;
537 -
538 - }
539 -
540 - private function drop(
541 - $dbs,
542 - $tbl
543 - ) {
544 -
545 - // All values have already been validated and sanitized in the rest route registration.
546 -
547 - if ( ! current_user_can( 'manage_options' ) ) {
548 - return 'Unauthorized';
549 - }
550 -
551 - $wpdadb = WPDADB::get_db_connection( $dbs );
552 - if ( null === $wpdadb ) {
553 - return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) );
554 - }
555 -
556 - $suppress_errors = $wpdadb->suppress_errors;
557 - $wpdadb->suppress_errors = true;
558 -
559 - $wpdadb->query(
560 - $wpdadb->prepare(
561 - 'drop table `%1s`',
562 - array(
563 - $tbl,
564 - )
565 - )
566 - );
567 -
568 - $wpdadb->suppress_errors = $suppress_errors;
569 -
570 - return $wpdadb->last_error;
571 -
572 - }
573 -
574 - }
575 -
576 -}
770 +}