PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.79
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.79
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 / Utilities / WPDA_Import_Multi.php

WPDA_Import_Multi.php in WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards 5.5.79, at WPDataAccess/Utilities/WPDA_Import_Multi.php

452 lines 14.4 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\Utilities
7 */
8
9 namespace WPDataAccess\Utilities {
10
11 use WPDataAccess\Connection\WPDADB;
12 use WPDataAccess\Dashboard\WPDA_Dashboard;
13 use WPDataAccess\WPDA;
14
15 /**
16 * Class WPDA_Import_Multi
17 *
18 * Imports a script file that might contain multiple SQL statement including create table and insert into
19 * statements.
20 *
21 * @author Peter Schulz
22 * @since 1.6.0
23 */
24 class WPDA_Import_Multi {
25
26 const SOLUTIONS = '(<a href="https://docs.wpdataaccess.com/limitations.html" target="_blank">see solutions</a>)';
27
28 /**
29 * URL where to post data
30 *
31 * @var string
32 */
33 protected $url;
34
35 /**
36 * Database schema name
37 *
38 * @var string
39 */
40 protected $schema_name;
41
42 /**
43 * Pointer to import file
44 *
45 * @var string
46 */
47 protected $file_pointer;
48
49 /**
50 * Content of import file
51 *
52 * @var string
53 */
54 protected $file_content;
55
56 /**
57 * Text to inform user (line 1).
58 *
59 * @var string
60 */
61 protected $info_title = '';
62
63 /**
64 * Text to inform user (line 1).
65 *
66 * @var string
67 */
68 protected $info_text = '';
69
70 /**
71 * Indicicates whether ZipArchive is installed.
72 *
73 * @var bool
74 */
75 protected $isinstalled_ziparchive = false;
76
77 /**
78 * WPDA_Import constructor
79 *
80 * Checks if ZipArchive is installed to support the import of ZIP files.
81 *
82 * @param string $page Page where to post data (url).
83 * @param string $schema_name Database schema name.
84 * @param array $args Extra arguments.
85 *
86 * @since 1.6.0
87 */
88 public function __construct( $page, $schema_name, $args = null ) {
89 $this->url = $page;
90 $this->schema_name = $schema_name;
91
92 $this->isinstalled_ziparchive = class_exists( '\ZipArchive' );
93
94 if ( ! is_null( $args ) && isset( $args[0] ) && isset( $args[1] ) ) {
95 $this->info_title = $args[0];
96 $this->info_text = $args[1];
97 } else {
98 $this->info_title = __(
99 'IMPORT DATA/EXECUTE SCRIPT(S)',
100 'wp-data-access'
101 );
102 if ( $this->isinstalled_ziparchive ) {
103 $this->info_text = __(
104 'Supports <strong>sql</strong> and <strong>zip</strong> file types.',
105 'wp-data-access'
106 );
107 } else {
108 $this->info_text = __(
109 'Supports only file type <strong>sql</strong> (install <strong>ZipArchive</strong> for <strong>zip</strong> file type support).',
110 'wp-data-access'
111 );
112 }
113 }
114 }
115
116 /**
117 * Checks if request is valid and allowed
118 *
119 * If the requested import is valid and allowed, the import file is loaded and its content imported.
120 *
121 * @since 1.6.0
122 */
123 public function check_post() {
124 // Check if import was requested.
125 if (
126 isset( $_REQUEST['action'] ) &&
127 'import' === sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ) // input var okay.
128 ) {
129 // Security check.
130 $wp_nonce = isset( $_REQUEST['_wpnonceimport'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonceimport'] ) ) : '?'; // input var okay.
131 if ( ! wp_verify_nonce( $wp_nonce, 'wpda-import-from-data-explorer-' . WPDA::get_current_user_login() ) ) {
132 wp_die( esc_attr__( 'ERROR: Not authorized', 'wp-data-access' ) );
133 }
134
135 if ( isset( $_FILES['filename'] ) ) {
136
137 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated
138 $temp_file_name = sanitize_text_field( $_FILES['filename']['tmp_name'] ); // For Windows: do NOT unslash!
139 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotValidated
140 $temp_file_type = sanitize_text_field( wp_unslash( $_FILES['filename']['type'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
141 $orig_file_name = sanitize_text_field( wp_unslash( $_FILES['filename']['name'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
142
143 if ( 0 === $_FILES['filename']['error'] && is_uploaded_file( $temp_file_name ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
144 if (
145 'application/zip' === $temp_file_type ||
146 'application/x-zip' === $temp_file_type ||
147 'application/x-zip-compressed' === $temp_file_type
148 ) {
149 // Process ZIP file.
150 if ( $this->isinstalled_ziparchive ) {
151 $zip = new \ZipArchive();
152 if ( $zip->open( $temp_file_name ) ) {
153 for ( $i = 0; $i < $zip->numFiles; $i ++ ) {
154 $this->file_pointer = $zip->getStream( $zip->getNameIndex( $i ) );
155 $this->import( $zip->getNameIndex( $i ) );
156 }
157 } else {
158 // Error reading ZIP file.
159 /* translators: %s = file name */
160 $this->import_failed( sprintf( __( 'Import failed [error reading ZIP file `%s`]', 'wp-data-access' ), $orig_file_name ) );
161 }
162 } else {
163 // ZipArchive not installed.
164 /* translators: %s = solution message */
165 $this->import_failed( sprintf( __( 'Import failed - ZipArchive not installed %s', 'wp-data-access' ), self::SOLUTIONS ) );
166 }
167 } else {
168 // Process plain file.
169 $this->file_pointer = fopen( $temp_file_name, 'rb' ); // phpcs:ignore
170 $this->import( $orig_file_name );
171 }
172 }
173 } else {
174 $this->upload_failed();
175 }
176 } elseif ( isset( $_REQUEST['impchk'] ) ) {
177 $this->upload_failed();
178 }
179 }
180
181 /**
182 * Perform import
183 *
184 * Import file is read in chunks to allow imports of large files without resource limitations. An indicator
185 * is maintained to follow the progress of the import. The indicator is used to show a message at the end
186 * whether the import succeeded or failed.
187 *
188 * @param string $file_name Import file name
189 *
190 * @since 1.6.0
191 */
192 protected function import( $file_name ) {
193 // Check if errors should be shown.
194 $hide_errors = isset( $_REQUEST['hide_errors'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['hide_errors'] ) ) : 'off'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- already verified
195
196 $result = true;
197 global $wpdb;
198
199 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
200 if ( null === $wpdadb ) {
201 if ( is_admin() ) {
202 /* translators: %s = database name */
203 wp_die( sprintf( esc_attr__( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) );
204 } else {
205 /* translators: %s = database name */
206 die( sprintf( esc_attr__( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) );
207 }
208 }
209
210 $suppress = $wpdadb->suppress_errors( 'on' === $hide_errors );
211
212 if ( false !== $this->file_pointer ) {
213 while ( ! feof( $this->file_pointer ) ) {
214 $this->file_content .= fread( $this->file_pointer, 4096 ); // phpcs:ignore
215
216 // Replace WP prefix and WPDA prefix.
217 $this->file_content = str_replace( '{wp_schema}', $wpdb->dbname, $this->file_content );
218 $this->file_content = str_replace( '{wp_prefix}', $wpdb->prefix, $this->file_content );
219 $this->file_content = str_replace( '{wpda_prefix}', 'wpda', $this->file_content ); // for backward compatibility
220
221 // Find and process SQL statements.
222 $sql_end_unix = strpos( $this->file_content, ";\n" );
223 $sql_end_windows = strpos( $this->file_content, ";\r\n" );
224 while ( false !== $sql_end_unix || false !== $sql_end_windows ) {
225 if ( false === $sql_end_unix ) {
226 $sql_end = $sql_end_windows;
227 } elseif ( false === $sql_end_windows ) {
228 $sql_end = $sql_end_unix;
229 } else {
230 $sql_end = min( $sql_end_unix, $sql_end_windows );
231 }
232 $sql = rtrim( substr( $this->file_content, 0, $sql_end ) );
233
234 $this->file_content = substr( $this->file_content, strpos( $this->file_content, $sql ) + strlen( $sql ) + 1 );
235
236 if ( false === $wpdadb->query( $sql ) ) {
237 $result = false;
238 }
239
240 // Find next SQL statement.
241 $sql_end_unix = strpos( $this->file_content, ";\n" );
242 $sql_end_windows = strpos( $this->file_content, ";\r\n" );
243 }
244 }
245 }
246
247 $wpdadb->suppress_errors( $suppress );
248
249 // Process file content.
250 if ( ! $result ) {
251 /* translators: %s = file name */
252 $this->import_failed( sprintf( __( 'Import `%s` failed [check import file]', 'wp-data-access' ), $file_name ) );
253 } else {
254 // Import succeeded.
255 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
256 $msg = new WPDA_Message_Box(
257 array(
258 /* translators: %s = file name */
259 'message_text' => sprintf( __( 'Import `%s` completed succesfully', 'wp-data-access' ), $file_name ),
260 )
261 );
262 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
263 $msg->box();
264 }
265 }
266
267 /**
268 * Inform user that an error occured
269 *
270 * @param string $msg Error message text
271 *
272 * @since 1.6.0
273 */
274 protected function import_failed( $msg ) {
275 $msg = new WPDA_Message_Box(
276 array(
277 'message_text' => $msg,
278 'message_type' => 'error',
279 'message_is_dismissible' => false,
280 )
281 );
282 $msg->box();
283 }
284
285 /**
286 * Inform user that the import failed
287 *
288 * @since 1.6.0
289 */
290 protected function upload_failed() {
291 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
292 $msg = new WPDA_Message_Box(
293 array(
294 /* translators: %s = solution message */
295 'message_text' => sprintf( __( 'File upload failed %s', 'wp-data-access' ), self::SOLUTIONS ),
296 'message_type' => 'error',
297 'message_is_dismissible' => false,
298 )
299 );
300 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
301 $msg->box();
302 }
303
304 /**
305 * Adds an import button
306 *
307 * @param string $label Button label.
308 * @param string $class Button CSS class.
309 *
310 * @since 1.6.0
311 */
312 public function add_button( $label = '', $class = 'page-title-action' ) {
313 if ( '' === $label ) {
314 $label = __( 'Imports and scripts', 'wp-data-access' );
315 }
316 ?>
317 <a href="javascript:void(0)"
318 onclick="jQuery('#upload_file_container_multi').show()"
319 class="wpda_tooltip <?php echo esc_attr( $class ); ?>"
320 title="<?php esc_html_e( 'Allows to import data and execute SQL scripts', 'wp-data-access' ); ?>.
321 <?php esc_html_e( 'Add a ; and a new line character at the end of every SQL statement', 'wp-data-access' ); ?>"
322 >
323 <i class="fas fa-code wpda_icon_on_button"></i> <?php echo esc_attr( $label ); ?></a>
324 <?php
325 }
326
327 /**
328 * Adds an import container
329 *
330 * The container contains an upload form. The container is hidden by default. When the button created in
331 * {@see WPDA_Import_Multi::add_button()} is clicked, the container is shown.
332 *
333 * @since 1.6.0
334 */
335 public function add_container() {
336 $file_uploads_enabled = @ini_get( 'file_uploads' );
337 ?>
338
339 <script type='text/javascript'>
340 function before_submit_upload() {
341 if (jQuery('#filename').val() == '') {
342 alert('<?php esc_html_e( 'No file to import!', 'wp-data-access' ); ?>');
343 return false;
344 }
345 if (!(jQuery('#filename')[0].files[0].size < <?php echo esc_attr( WPDA::convert_memory_to_decimal( @ini_get( 'upload_max_filesize' ) ) ); ?>)) {
346 alert("<?php esc_html_e( 'File exceeds maximum size of', 'wp-data-access' ); ?> <?php echo esc_attr( @ini_get( 'upload_max_filesize' ) ); ?>!");
347 return false;
348 }
349 }
350 function activeTab(tabIndex) {
351 jQuery(".import_tab").hide();
352 jQuery(".nav-tab").removeClass("nav-tab-active");
353 jQuery("#import_tab-" + tabIndex).show();
354 jQuery("#import_tabs-" + tabIndex).addClass("nav-tab-active");
355 }
356 </script>
357
358 <div id="upload_file_container_multi" style="display: none">
359 <div>&nbsp;</div>
360 <div>
361 <div class="wpda_upload_container_dashboard">
362 <?php
363 if ( $file_uploads_enabled ) {
364 ?>
365 <form id="form_import_multi_table" method="post"
366 action="<?php echo esc_attr( $this->url ); ?>&impchk"
367 enctype="multipart/form-data">
368 <?php
369 echo $this->import_sql(); // phpcs:ignore WordPress.Security.EscapeOutput
370 ?>
371 </form>
372 <?php
373 } else {
374 ?>
375 <p>
376 <strong><?php esc_html_e( 'ERROR', 'wp-data-access' ); ?></strong>
377 </p>
378 <p class="wpda_list_indent">
379 <?php
380 esc_html_e( 'Your configuration does not allow file uploads!', 'wp-data-access' );
381 echo ' ';
382 esc_html_e( 'Set', 'wp-data-access' );
383 echo ' <strong>';
384 esc_html_e( 'file_uploads', 'wp-data-access' );
385 echo '</strong> ';
386 esc_html_e( 'to', 'wp-data-access' );
387 echo ' <strong>';
388 esc_html_e( 'On', 'wp-data-access' );
389 echo '</strong> (<a href="https://docs.wpdataaccess.com/limitations.html">';
390 esc_html_e( 'see documentation', 'wp-data-access' );
391 echo '</a>).';
392 ?>
393 </p>
394 <?php
395 }
396 ?>
397 </div>
398 </div>
399 <div>&nbsp;</div>
400 </div>
401 <?php
402 }
403
404 private function import_sql() {
405 if ( $this->isinstalled_ziparchive ) {
406 $file_extensions = '.sql,.zip';
407 } else {
408 $file_extensions = '.sql';
409 }
410 ?>
411 <p class="wpda_list_indent">
412 <?php
413 echo $this->info_text . ' ' . esc_attr__( 'Maximum supported file size is', 'wp-data-access' ) . ' <strong>' . @ini_get( 'upload_max_filesize' ) . '</strong>. '; // phpcs:ignore WordPress.Security.EscapeOutput
414 ?>
415 </p>
416 <p class="wpda_list_indent">
417 <input type="file" name="filename" id="filename" class="wpda_tooltip"
418 accept="<?php echo esc_attr( $file_extensions ); ?>">
419 <label style="vertical-align:baseline;">
420 <input type="checkbox" name="hide_errors" style="vertical-align:sub;" checked>
421 <?php esc_html_e( 'Hide errors', 'wp-data-access' ); ?>
422 </label>
423 </p>
424 <p class="wpda_list_indent">
425 <button type="submit"
426 class="button button-primary"
427 onclick="return before_submit_upload()">
428 <i class="fas fa-code wpda_icon_on_button"></i>
429 <?php esc_html_e( 'Import file/Execute script(s)', 'wp-data-access' ); ?>
430 </button>
431 <a href="javascript:void(0)"
432 onclick="jQuery('#upload_file_container_multi').hide()"
433 class="button button-secondary">
434 <i class="fas fa-times-circle wpda_icon_on_button"></i>
435 <?php esc_html_e( 'Cancel', 'wp-data-access' ); ?>
436 </a>
437 <input type="hidden" name="action" value="import">
438 <?php wp_nonce_field( 'wpda-import-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnonceimport', false ); ?>
439 </p>
440 <p class="wpda_list_indent">
441 <?php
442 echo '<strong>' . esc_attr__( 'IMPORTANT', 'wp-data-access' ) . '</strong> &minus; ' .
443 esc_attr__( 'Add a ; and a new line character at the end of every SQL statement', 'wp-data-access' );
444 ?>
445 </p>
446 <?php
447 }
448
449 }
450
451 }
452