PluginProbe
WebberZone Top 10 — Popular Posts / trunk
WebberZone Top 10 — Popular Posts vtrunk
4.5.0 4.4.3 4.4.2 4.4.1 4.4.0 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 trunk 1.0 1.0.1 1.1 1.2 1.3 1.4 1.4.1 1.5 1.5.1 1.5.2 1.5.3 1.6 1.6.1 1.6.2 All 116 releases
top-10 / includes / admin / class-import-export.php

class-import-export.php in WebberZone Top 10 — Popular Posts trunk, at includes/admin/class-import-export.php

581 lines 18.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Import Export class.
4 *
5 * @package WebberZone\Top_Ten\Admin
6 */
7
8 namespace WebberZone\Top_Ten\Admin;
9
10 use WebberZone\Top_Ten\Database;
11 use WebberZone\Top_Ten\Util\Helpers;
12 use WebberZone\Top_Ten\Util\Hook_Registry;
13 use WebberZone\Top_Ten\Util\Csv_Helper;
14
15 if ( ! defined( 'WPINC' ) ) {
16 die;
17 }
18
19 /**
20 * Import Export functions.
21 *
22 * @since 3.3.0
23 */
24 class Import_Export {
25
26 /**
27 * Parent Menu ID.
28 *
29 * @since 3.3.0
30 *
31 * @var string Parent Menu ID.
32 */
33 public $parent_id;
34
35 /**
36 * Constructor class.
37 *
38 * @since 3.3.0
39 */
40 public function __construct() {
41 Hook_Registry::add_filter( 'admin_init', array( $this, 'process_settings_import' ), 9 );
42 Hook_Registry::add_filter( 'admin_init', array( $this, 'process_settings_export' ) );
43 Hook_Registry::add_filter( 'admin_init', array( $this, 'export_tables' ) );
44 Hook_Registry::add_filter( 'admin_init', array( $this, 'import_tables' ), 9 );
45 Hook_Registry::add_action( 'admin_menu', array( $this, 'admin_menu' ) );
46 Hook_Registry::add_action( 'network_admin_menu', array( $this, 'network_admin_menu' ), 11 );
47 Hook_Registry::add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
48
49 new WPP_Importer();
50 }
51
52 /**
53 * Enqueue scripts in admin area.
54 *
55 * @since 4.0.0
56 *
57 * @param string $hook The current admin page.
58 */
59 public function admin_enqueue_scripts( $hook ) {
60 if ( $hook === $this->parent_id ) {
61 wp_enqueue_script( 'top-ten-admin-js' );
62 wp_enqueue_style( 'top-ten-admin-css' );
63
64 }
65 }
66
67 /**
68 * Admin Menu.
69 *
70 * @since 3.3.0
71 */
72 public function admin_menu() {
73
74 $this->parent_id = add_submenu_page(
75 'tptn_dashboard',
76 esc_html__( 'Top 10 Import Export Tables', 'top-10' ),
77 esc_html__( 'Import/Export', 'top-10' ),
78 'manage_options',
79 'tptn_exim_page',
80 array( $this, 'render_page' )
81 );
82 }
83
84 /**
85 * Admin Menu.
86 *
87 * @since 3.3.0
88 */
89 public function network_admin_menu() {
90
91 $this->parent_id = add_submenu_page(
92 'tptn_dashboard',
93 esc_html__( 'Top 10 Import Export Tables', 'top-10' ),
94 esc_html__( 'Import/Export', 'top-10' ),
95 'manage_network_options',
96 'tptn_exim_page',
97 array( $this, 'render_page' )
98 );
99 }
100 /**
101 * Render the tools settings page.
102 *
103 * @since 2.7.0
104 *
105 * @return void
106 */
107 public function render_page() {
108
109 /* Message for successful file import */
110 if ( isset( $_GET['file_import'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
111 if ( 'success' === $_GET['file_import'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
112 add_settings_error( 'tptn-notices', '', esc_html__( 'Data has been imported into the table', 'top-10' ), 'success' );
113 } elseif ( 'fail' === $_GET['file_import'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
114 add_settings_error( 'tptn-notices', '', esc_html__( 'Data import failure. Check the number of columns for the file being imported, if you are uploading it in the right section below and that the data is correct', 'top-10' ), 'error' );
115 }
116 }
117
118 /* Message for successful file import */
119 if ( isset( $_GET['settings_import'] ) && 'success' === $_GET['settings_import'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
120 add_settings_error( 'tptn-notices', '', esc_html__( 'Settings have been imported successfully', 'top-10' ), 'success' );
121 }
122
123 ob_start();
124 ?>
125 <div class="wrap">
126 <h1><?php esc_html_e( 'Top 10 - Import/Export tables', 'top-10' ); ?></h1>
127 <?php
128 /** This action is documented in includes/admin/class-dashboard.php */
129 do_action( 'tptn_settings_page_header' );
130 ?>
131
132 <?php settings_errors(); ?>
133
134 <div id="poststuff">
135 <div id="post-body" class="metabox-holder columns-2">
136 <div id="post-body-content">
137
138 <?php if ( ! is_network_admin() ) : ?>
139 <div class="postbox">
140 <h2><span><?php esc_html_e( 'Export/Import settings', 'top-10' ); ?></span></h2>
141 <div class="inside">
142 <form method="post">
143 <p class="description">
144 <?php esc_html_e( 'Export the plugin settings for this site as a .json file. This allows you to easily import the configuration into another site.', 'top-10' ); ?>
145 </p>
146 <p><input type="hidden" name="tptn_action" value="export_settings" /></p>
147 <p>
148 <?php submit_button( esc_html__( 'Export Settings', 'top-10' ), 'primary', 'tptn_export_settings', false ); ?>
149 </p>
150
151 <?php wp_nonce_field( 'tptn_export_settings_nonce', 'tptn_export_settings_nonce' ); ?>
152 </form>
153
154 <form method="post" enctype="multipart/form-data">
155 <p class="description">
156 <?php esc_html_e( 'Import the plugin settings from a .json file. This file can be obtained by exporting the settings on this/another site using the form above.', 'top-10' ); ?>
157 </p>
158 <p>
159 <input type="file" name="import_settings_file" />
160 </p>
161 <p>
162 <?php submit_button( esc_html__( 'Import Settings', 'top-10' ), 'primary', 'tptn_import_settings', false ); ?>
163 </p>
164
165 <input type="hidden" name="tptn_action" value="import_settings" />
166 <?php wp_nonce_field( 'tptn_import_settings_nonce', 'tptn_import_settings_nonce' ); ?>
167 </form>
168 </div>
169 </div>
170 <?php endif; ?>
171
172 <div class="postbox">
173 <h2><span><?php esc_html_e( 'Export tables', 'top-10' ); ?></span></h2>
174 <div class="inside">
175 <form method="post">
176 <p class="description">
177 <?php esc_html_e( 'Click the buttons below to export the overall and the daily tables. The file is downloaded as an CSV file which you should be able to edit in Excel or any other compatible software.', 'top-10' ); ?>
178 <?php esc_html_e( 'If you are using WordPress Multisite then this will include the counts across all sites as the plugin uses a single table to store counts.', 'top-10' ); ?>
179 </p>
180 <p>
181 <label><input type="checkbox" name="export_urls" id="export_urls" value="1" /> <?php esc_html_e( 'Include URLs in export', 'top-10' ); ?></label>
182 <input type="hidden" name="tptn_action" value="export_tables" />
183 <input type="hidden" name="network_wide" value="<?php echo ( is_network_admin() ? 1 : 0 ); ?>" />
184 </p>
185 <p>
186 <?php submit_button( esc_html__( 'Export overall tables', 'top-10' ), 'primary', 'tptn_export_total', false ); ?>
187 <?php submit_button( esc_html__( 'Export daily tables', 'top-10' ), 'primary', 'tptn_export_daily', false ); ?>
188 </p>
189
190 <?php wp_nonce_field( 'tptn_export_nonce', 'tptn_export_nonce' ); ?>
191 </form>
192 </div>
193 </div>
194
195 <div class="postbox">
196 <h2><span><?php esc_html_e( 'Import tables', 'top-10' ); ?></span></h2>
197 <div class="inside">
198 <form method="post" enctype="multipart/form-data">
199 <p class="description">
200 <?php esc_html_e( 'Importing a file will add or increment counts — it does not replace existing data. To start fresh, use the Maintenance tab to truncate the tables first. Best practice is to export before importing. It is important to maintain the export format of the data to avoid corruption.', 'top-10' ); ?>
201 <br />
202 <?php esc_html_e( 'Be careful when opening the file in Excel as it tends to change the date format. Recommended date-time format is YYYY-MM-DD H.', 'top-10' ); ?>
203 </p>
204 <p class="notice notice-warning">
205 <strong><?php esc_html_e( 'Backup your database before proceeding so you will be able to restore it in case anything goes wrong.', 'top-10' ); ?></strong>
206 </p>
207 <p>
208 <label><input type="checkbox" name="import_urls" id="import_urls" value="1" /> <?php esc_html_e( 'Use URLs instead of Post IDs in import', 'top-10' ); ?></label>
209 <input type="hidden" name="tptn_action" value="import_tables" />
210 <input type="hidden" name="network_wide" value="<?php echo ( is_network_admin() ? 1 : 0 ); ?>" />
211 </p>
212
213 <table class="form-table">
214 <tr>
215 <th scope="row"><?php esc_html_e( 'Import overall tables', 'top-10' ); ?></th>
216 <td><input type="file" name="overall_table_file" /><br />
217 <span class="description"><?php esc_html_e( 'CSV file', 'top-10' ); ?></span></td>
218 </tr>
219 <tr>
220 <th scope="row"><?php esc_html_e( 'Import daily tables', 'top-10' ); ?></th>
221 <td><input type="file" name="daily_table_file" /><br />
222 <span class="description"><?php esc_html_e( 'CSV file', 'top-10' ); ?></span></td>
223 </tr>
224 </table>
225 <p>
226 <?php submit_button( esc_html__( 'Import Tables', 'top-10' ), 'primary', 'tptn_import', false ); ?>
227 </p>
228
229 <?php wp_nonce_field( 'tptn_import_nonce', 'tptn_import_nonce' ); ?>
230 </form>
231 </div>
232 </div>
233
234 <?php
235 /**
236 * Action hook to add additional import/export options.
237 *
238 * @since 3.3.0
239 */
240 do_action( 'tptn_admin_import_export_tab_content' );
241 ?>
242
243 </div><!-- /#post-body-content -->
244
245 <div id="postbox-container-1" class="postbox-container">
246
247 <div id="side-sortables" class="meta-box-sortables ui-sortable">
248 <?php include_once 'sidebar.php'; ?>
249 </div><!-- /#side-sortables -->
250
251 </div><!-- /#postbox-container-1 -->
252 </div><!-- /#post-body -->
253 <br class="clear" />
254 </div><!-- /#poststuff -->
255
256 </div><!-- /.wrap -->
257
258 <?php
259 echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
260 }
261
262
263 /**
264 * Process a settings export that generates a .csv file of the Top 10 table.
265 *
266 * @since 2.7.0
267 */
268 public static function export_tables() {
269 global $wpdb;
270
271 if ( empty( $_POST['tptn_action'] ) || 'export_tables' !== $_POST['tptn_action'] ) {
272 return;
273 }
274
275 if ( ! wp_verify_nonce( sanitize_key( $_POST['tptn_export_nonce'] ), 'tptn_export_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
276 return;
277 }
278
279 if ( is_network_admin() && ! current_user_can( 'manage_network_options' ) ) {
280 return;
281 }
282
283 if ( is_admin() && ! current_user_can( 'manage_options' ) ) {
284 return;
285 }
286
287 if ( isset( $_POST['tptn_export_total'] ) ) {
288 $daily = false;
289 } elseif ( isset( $_POST['tptn_export_daily'] ) ) {
290 $daily = true;
291 } else {
292 return;
293 }
294
295 if ( isset( $_POST['network_wide'] ) ) {
296 $network_wide = intval( $_POST['network_wide'] );
297 } else {
298 return;
299 }
300
301 if ( isset( $_POST['export_urls'] ) ) {
302 $export_urls = intval( $_POST['export_urls'] );
303 } else {
304 $export_urls = 0;
305 }
306
307 $table_name = Database::get_table( $daily );
308
309 $filename = array(
310 'top-ten',
311 ( $daily ? 'daily' : '' ),
312 'table',
313 ( is_network_admin() ? '' : 'blog' . get_current_blog_id() ),
314 current_time( 'Y_m_d_Hi' ),
315 );
316 $filename = array_filter( $filename );
317 $filename = implode( '-', $filename );
318 $filename = $filename . '.csv';
319
320 $results = Csv_Helper::fetch_export_data( $daily, (bool) $network_wide, get_current_blog_id() );
321
322 ignore_user_abort( true );
323
324 nocache_headers();
325 header( 'Content-Type: text/csv; charset=utf-8' );
326 header( "Content-Disposition: attachment; filename={$filename}" );
327 header( 'Expires: 0' );
328
329 $fh = fopen( 'php://output', 'w' );
330 Csv_Helper::write_export_csv( $fh, $results, $daily, (bool) $export_urls, (bool) $network_wide );
331 fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
332
333 exit;
334 }
335
336 /**
337 * Process a .csv file to import the table into the database.
338 *
339 * @since 2.7.0
340 */
341 public static function import_tables() {
342 if ( empty( $_POST['tptn_action'] ) || 'import_tables' !== $_POST['tptn_action'] ) {
343 return;
344 }
345
346 if ( ! wp_verify_nonce( sanitize_key( $_POST['tptn_import_nonce'] ), 'tptn_import_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
347 return;
348 }
349
350 if ( is_network_admin() && ! current_user_can( 'manage_network_options' ) ) {
351 return;
352 }
353
354 if ( is_admin() && ! current_user_can( 'manage_options' ) ) {
355 return;
356 }
357
358 $overall_upload = ! empty( $_FILES['overall_table_file']['tmp_name'] );
359 $daily_upload = ! empty( $_FILES['daily_table_file']['tmp_name'] );
360
361 if ( ! $overall_upload && ! $daily_upload ) {
362 return;
363 }
364
365 if ( isset( $_POST['network_wide'] ) ) {
366 $network_wide = (bool) intval( $_POST['network_wide'] );
367 } else {
368 return;
369 }
370
371 if ( isset( $_POST['import_urls'] ) ) {
372 $use_urls = (bool) intval( $_POST['import_urls'] );
373 } else {
374 $use_urls = false;
375 }
376
377 $results = array();
378
379 if ( $overall_upload ) {
380 $import_file = wp_unslash( $_FILES['overall_table_file']['tmp_name'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
381 $parsed = Csv_Helper::parse_import_file( $import_file );
382
383 if ( 0 === $parsed['total'] ) {
384 wp_safe_redirect(
385 add_query_arg(
386 array(
387 'page' => 'tptn_exim_page',
388 'file_import' => 'fail',
389 ),
390 is_network_admin() ? network_admin_url( 'admin.php' ) : admin_url( 'admin.php' )
391 )
392 );
393 exit;
394 }
395
396 $prepared = self::build_import_data( $parsed['rows'], false, $use_urls, $network_wide );
397 if ( ! empty( $prepared ) ) {
398 $results[] = Database::bulk_upsert( $prepared, false );
399 }
400 }
401
402 if ( $daily_upload ) {
403 $import_file = wp_unslash( $_FILES['daily_table_file']['tmp_name'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
404 $parsed = Csv_Helper::parse_import_file( $import_file );
405
406 if ( 0 === $parsed['total'] ) {
407 wp_safe_redirect(
408 add_query_arg(
409 array(
410 'page' => 'tptn_exim_page',
411 'file_import' => 'fail',
412 ),
413 is_network_admin() ? network_admin_url( 'admin.php' ) : admin_url( 'admin.php' )
414 )
415 );
416 exit;
417 }
418
419 $prepared = self::build_import_data( $parsed['rows'], true, $use_urls, $network_wide );
420 if ( ! empty( $prepared ) ) {
421 $results[] = Database::bulk_upsert( $prepared, true );
422 }
423 }
424
425 $all_success = ! empty( $results ) && ! in_array( false, $results, true );
426 $file_import = $all_success ? 'success' : 'fail';
427
428 wp_safe_redirect(
429 add_query_arg(
430 array(
431 'page' => 'tptn_exim_page',
432 'file_import' => $file_import,
433 ),
434 is_network_admin() ? network_admin_url( 'admin.php' ) : admin_url( 'admin.php' )
435 )
436 );
437 exit;
438 }
439
440 /**
441 * Build structured import data from parsed CSV rows.
442 *
443 * @since 4.3.0
444 *
445 * @param array $rows Parsed rows from Csv_Helper::parse_import_file().
446 * @param bool $daily Whether these are daily-table rows.
447 * @param bool $use_urls Whether to resolve URL column to post IDs.
448 * @param bool $network_wide Whether this is a network-wide import.
449 * @return array Prepared data rows for Database::bulk_upsert().
450 */
451 private static function build_import_data( array $rows, bool $daily, bool $use_urls, bool $network_wide ): array {
452 $prepared = array();
453 $url_list = array();
454
455 foreach ( $rows as $row ) {
456 $post_id = absint( $row['postnumber'] );
457 $blog_id = isset( $row['blog_id'] ) ? absint( $row['blog_id'] ) : get_current_blog_id();
458
459 if ( $use_urls && ! empty( $row['url'] ) ) {
460 $url = esc_url_raw( trim( $row['url'], " \t\n\r\0\x0B" ) );
461 if ( ! isset( $url_list[ $url ] ) ) {
462 $url_list[ $url ] = url_to_postid( $url );
463 }
464 $post_id = absint( $url_list[ $url ] );
465 }
466
467 if ( 0 === $post_id ) {
468 continue;
469 }
470
471 if ( ! $network_wide && ! is_network_admin() && (int) get_current_blog_id() !== $blog_id ) {
472 continue;
473 }
474
475 if ( $daily ) {
476 $raw_date = isset( $row['dp_date'] ) ? trim( $row['dp_date'], " \t\n\r\0\x0B" ) : '';
477 try {
478 $dp_date = ( new \DateTime( $raw_date ) )->format( 'Y-m-d H' );
479 } catch ( \Exception $e ) {
480 $dp_date = $raw_date;
481 }
482 $prepared[] = array(
483 'postnumber' => $post_id,
484 'cntaccess' => absint( $row['cntaccess'] ),
485 'dp_date' => $dp_date,
486 'blog_id' => $blog_id,
487 );
488 } else {
489 $prepared[] = array(
490 'postnumber' => $post_id,
491 'cntaccess' => absint( $row['cntaccess'] ),
492 'blog_id' => $blog_id,
493 );
494 }
495 }
496
497 return $prepared;
498 }
499
500 /**
501 * Process a settings export that generates a .json file of the Top 10 settings
502 *
503 * @since 2.7.0
504 */
505 public static function process_settings_export() {
506
507 if ( empty( $_POST['tptn_action'] ) || 'export_settings' !== $_POST['tptn_action'] ) {
508 return;
509 }
510
511 if ( ! wp_verify_nonce( sanitize_key( $_POST['tptn_export_settings_nonce'] ), 'tptn_export_settings_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
512 return;
513 }
514
515 if ( ! current_user_can( 'manage_options' ) ) {
516 return;
517 }
518
519 $settings = get_option( 'tptn_settings' );
520
521 ignore_user_abort( true );
522
523 nocache_headers();
524 header( 'Content-Type: application/json; charset=utf-8' );
525 header( 'Content-Disposition: attachment; filename=tptn-settings-export-' . gmdate( 'm-d-Y' ) . '.json' );
526 header( 'Expires: 0' );
527
528 echo wp_json_encode( $settings );
529 exit;
530 }
531
532 /**
533 * Process a settings import from a json file
534 *
535 * @since 2.7.0
536 */
537 public static function process_settings_import() {
538
539 if ( empty( $_POST['tptn_action'] ) || 'import_settings' !== $_POST['tptn_action'] ) {
540 return;
541 }
542
543 if ( ! wp_verify_nonce( sanitize_key( $_POST['tptn_import_settings_nonce'] ), 'tptn_import_settings_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
544 return;
545 }
546
547 if ( ! current_user_can( 'manage_options' ) ) {
548 return;
549 }
550
551 $filename = 'import_settings_file';
552 $extension = isset( $_FILES[ $filename ]['name'] ) ? pathinfo( sanitize_file_name( wp_unslash( $_FILES[ $filename ]['name'] ) ), PATHINFO_EXTENSION ) : '';
553
554 if ( 'json' !== $extension ) {
555 wp_die( esc_html__( 'Please upload a valid .json file', 'top-10' ) );
556 }
557
558 $import_file = isset( $_FILES[ $filename ]['tmp_name'] ) ? ( wp_unslash( $_FILES[ $filename ]['tmp_name'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
559
560 if ( empty( $import_file ) ) {
561 wp_die( esc_html__( 'Please upload a file to import', 'top-10' ) );
562 }
563
564 // Retrieve the settings from the file and convert the json object to an array.
565 $settings = (array) json_decode( file_get_contents( $import_file ), true ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
566
567 update_option( 'tptn_settings', $settings );
568
569 wp_safe_redirect(
570 add_query_arg(
571 array(
572 'page' => 'tptn_exim_page',
573 'settings_import' => 'success',
574 ),
575 admin_url( 'admin.php' )
576 )
577 );
578 exit;
579 }
580 }
581