Helpers.php (21595B)
1 <?php 2 /** 3 * Static functions used in the OCDI plugin. 4 * 5 * @package ocdi 6 */ 7 8 namespace OCDI; 9 10 /** 11 * Class with static helper functions. 12 */ 13 class Helpers { 14 /** 15 * Holds the date and time string for demo import and log file. 16 * 17 * @var string 18 */ 19 public static $demo_import_start_time = ''; 20 21 /** 22 * Filter through the array of import files and get rid of those who do not comply. 23 * 24 * @param array $import_files list of arrays with import file details. 25 * @return array list of filtered arrays. 26 */ 27 public static function validate_import_file_info( $import_files ) { 28 $filtered_import_file_info = array(); 29 30 foreach ( $import_files as $import_file ) { 31 if ( self::is_import_file_info_format_correct( $import_file ) ) { 32 $filtered_import_file_info[] = $import_file; 33 } 34 } 35 36 return $filtered_import_file_info; 37 } 38 39 40 /** 41 * Helper function: a simple check for valid import file format. 42 * 43 * @param array $import_file_info array with import file details. 44 * @return boolean 45 */ 46 private static function is_import_file_info_format_correct( $import_file_info ) { 47 if ( empty( $import_file_info['import_file_name'] ) ) { 48 return false; 49 } 50 51 return true; 52 } 53 54 55 /** 56 * Download import files. Content .xml and widgets .wie|.json files. 57 * 58 * @param array $import_file_info array with import file details. 59 * @return array|WP_Error array of paths to the downloaded files or WP_Error object with error message. 60 */ 61 public static function download_import_files( $import_file_info ) { 62 $downloaded_files = array( 63 'content' => '', 64 'widgets' => '', 65 'customizer' => '', 66 'redux' => '', 67 ); 68 $downloader = new Downloader(); 69 70 // ----- Set content file path ----- 71 // Check if 'import_file_url' is not defined. That would mean a local file. 72 if ( empty( $import_file_info['import_file_url'] ) ) { 73 if ( file_exists( $import_file_info['local_import_file'] ) ) { 74 $downloaded_files['content'] = $import_file_info['local_import_file']; 75 } 76 } 77 else { 78 // Set the filename string for content import file. 79 $content_filename = apply_filters( 'pt-ocdi/downloaded_content_file_prefix', 'demo-content-import-file_' ) . self::$demo_import_start_time . apply_filters( 'pt-ocdi/downloaded_content_file_suffix_and_file_extension', '.xml' ); 80 81 // Download the content import file. 82 $downloaded_files['content'] = $downloader->download_file( $import_file_info['import_file_url'], $content_filename ); 83 84 // Return from this function if there was an error. 85 if ( is_wp_error( $downloaded_files['content'] ) ) { 86 return $downloaded_files['content']; 87 } 88 } 89 90 // ----- Set widget file path ----- 91 // Get widgets file as well. If defined! 92 if ( ! empty( $import_file_info['import_widget_file_url'] ) ) { 93 // Set the filename string for widgets import file. 94 $widget_filename = apply_filters( 'pt-ocdi/downloaded_widgets_file_prefix', 'demo-widgets-import-file_' ) . self::$demo_import_start_time . apply_filters( 'pt-ocdi/downloaded_widgets_file_suffix_and_file_extension', '.json' ); 95 96 // Download the widgets import file. 97 $downloaded_files['widgets'] = $downloader->download_file( $import_file_info['import_widget_file_url'], $widget_filename ); 98 99 // Return from this function if there was an error. 100 if ( is_wp_error( $downloaded_files['widgets'] ) ) { 101 return $downloaded_files['widgets']; 102 } 103 } 104 else if ( ! empty( $import_file_info['local_import_widget_file'] ) ) { 105 if ( file_exists( $import_file_info['local_import_widget_file'] ) ) { 106 $downloaded_files['widgets'] = $import_file_info['local_import_widget_file']; 107 } 108 } 109 110 // ----- Set customizer file path ----- 111 // Get customizer import file as well. If defined! 112 if ( ! empty( $import_file_info['import_customizer_file_url'] ) ) { 113 // Setup filename path to save the customizer content. 114 $customizer_filename = apply_filters( 'pt-ocdi/downloaded_customizer_file_prefix', 'demo-customizer-import-file_' ) . self::$demo_import_start_time . apply_filters( 'pt-ocdi/downloaded_customizer_file_suffix_and_file_extension', '.dat' ); 115 116 // Download the customizer import file. 117 $downloaded_files['customizer'] = $downloader->download_file( $import_file_info['import_customizer_file_url'], $customizer_filename ); 118 119 // Return from this function if there was an error. 120 if ( is_wp_error( $downloaded_files['customizer'] ) ) { 121 return $downloaded_files['customizer']; 122 } 123 } 124 else if ( ! empty( $import_file_info['local_import_customizer_file'] ) ) { 125 if ( file_exists( $import_file_info['local_import_customizer_file'] ) ) { 126 $downloaded_files['customizer'] = $import_file_info['local_import_customizer_file']; 127 } 128 } 129 130 // ----- Set Redux file paths ----- 131 // Get Redux import file as well. If defined! 132 if ( ! empty( $import_file_info['import_redux'] ) && is_array( $import_file_info['import_redux'] ) ) { 133 $redux_items = array(); 134 135 // Setup filename paths to save the Redux content. 136 foreach ( $import_file_info['import_redux'] as $index => $redux_item ) { 137 $redux_filename = apply_filters( 'pt-ocdi/downloaded_redux_file_prefix', 'demo-redux-import-file_' ) . $index . '-' . self::$demo_import_start_time . apply_filters( 'pt-ocdi/downloaded_redux_file_suffix_and_file_extension', '.json' ); 138 139 // Download the Redux import file. 140 $file_path = $downloader->download_file( $redux_item['file_url'], $redux_filename ); 141 142 // Return from this function if there was an error. 143 if ( is_wp_error( $file_path ) ) { 144 return $file_path; 145 } 146 147 $redux_items[] = array( 148 'option_name' => $redux_item['option_name'], 149 'file_path' => $file_path, 150 ); 151 } 152 153 // Download the Redux import file. 154 $downloaded_files['redux'] = $redux_items; 155 } 156 else if ( ! empty( $import_file_info['local_import_redux'] ) ) { 157 158 $redux_items = array(); 159 160 // Setup filename paths to save the Redux content. 161 foreach ( $import_file_info['local_import_redux'] as $redux_item ) { 162 if ( file_exists( $redux_item['file_path'] ) ) { 163 $redux_items[] = $redux_item; 164 } 165 } 166 167 // Download the Redux import file. 168 $downloaded_files['redux'] = $redux_items; 169 } 170 171 return $downloaded_files; 172 } 173 174 175 /** 176 * Write content to a file. 177 * 178 * @param string $content content to be saved to the file. 179 * @param string $file_path file path where the content should be saved. 180 * @return string|WP_Error path to the saved file or WP_Error object with error message. 181 */ 182 public static function write_to_file( $content, $file_path ) { 183 // Verify WP file-system credentials. 184 $verified_credentials = self::check_wp_filesystem_credentials(); 185 186 if ( is_wp_error( $verified_credentials ) ) { 187 return $verified_credentials; 188 } 189 190 // By this point, the $wp_filesystem global should be working, so let's use it to create a file. 191 global $wp_filesystem; 192 193 if ( ! $wp_filesystem->put_contents( $file_path, $content ) ) { 194 return new \WP_Error( 195 'failed_writing_file_to_server', 196 sprintf( 197 __( 'An error occurred while writing file to your server! Tried to write a file to: %s%s.', 'pt-ocdi' ), 198 '<br>', 199 $file_path 200 ) 201 ); 202 } 203 204 // Return the file path on successful file write. 205 return $file_path; 206 } 207 208 209 /** 210 * Append content to the file. 211 * 212 * @param string $content content to be saved to the file. 213 * @param string $file_path file path where the content should be saved. 214 * @param string $separator_text separates the existing content of the file with the new content. 215 * @return boolean|WP_Error, path to the saved file or WP_Error object with error message. 216 */ 217 public static function append_to_file( $content, $file_path, $separator_text = '' ) { 218 // Verify WP file-system credentials. 219 $verified_credentials = self::check_wp_filesystem_credentials(); 220 221 if ( is_wp_error( $verified_credentials ) ) { 222 return $verified_credentials; 223 } 224 225 // By this point, the $wp_filesystem global should be working, so let's use it to create a file. 226 global $wp_filesystem; 227 228 $existing_data = ''; 229 if ( file_exists( $file_path ) ) { 230 $existing_data = $wp_filesystem->get_contents( $file_path ); 231 } 232 233 // Style separator. 234 $separator = PHP_EOL . '---' . $separator_text . '---' . PHP_EOL; 235 236 if ( ! $wp_filesystem->put_contents( $file_path, $existing_data . $separator . $content . PHP_EOL ) ) { 237 return new \WP_Error( 238 'failed_writing_file_to_server', 239 sprintf( 240 __( 'An error occurred while writing file to your server! Tried to write a file to: %s%s.', 'pt-ocdi' ), 241 '<br>', 242 $file_path 243 ) 244 ); 245 } 246 247 return true; 248 } 249 250 251 /** 252 * Get data from a file 253 * 254 * @param string $file_path file path where the content should be saved. 255 * @return string $data, content of the file or WP_Error object with error message. 256 */ 257 public static function data_from_file( $file_path ) { 258 // Verify WP file-system credentials. 259 $verified_credentials = self::check_wp_filesystem_credentials(); 260 261 if ( is_wp_error( $verified_credentials ) ) { 262 return $verified_credentials; 263 } 264 265 // By this point, the $wp_filesystem global should be working, so let's use it to read a file. 266 global $wp_filesystem; 267 268 $data = $wp_filesystem->get_contents( $file_path ); 269 270 if ( ! $data ) { 271 return new \WP_Error( 272 'failed_reading_file_from_server', 273 sprintf( 274 __( 'An error occurred while reading a file from your server! Tried reading file from path: %s%s.', 'pt-ocdi' ), 275 '<br>', 276 $file_path 277 ) 278 ); 279 } 280 281 // Return the file data. 282 return $data; 283 } 284 285 286 /** 287 * Helper function: check for WP file-system credentials needed for reading and writing to a file. 288 * 289 * @return boolean|WP_Error 290 */ 291 private static function check_wp_filesystem_credentials() { 292 // Check if the file-system method is 'direct', if not display an error. 293 if ( ! ( 'direct' === get_filesystem_method() ) ) { 294 return new \WP_Error( 295 'no_direct_file_access', 296 sprintf( 297 __( 'This WordPress page does not have %sdirect%s write file access. This plugin needs it in order to save the demo import xml file to the upload directory of your site. You can change this setting with these instructions: %s.', 'pt-ocdi' ), 298 '<strong>', 299 '</strong>', 300 '<a href="http://gregorcapuder.com/wordpress-how-to-set-direct-filesystem-method/" target="_blank">How to set <strong>direct</strong> filesystem method</a>' 301 ) 302 ); 303 } 304 305 // Get plugin page settings. 306 $plugin_page_setup = apply_filters( 'pt-ocdi/plugin_page_setup', array( 307 'parent_slug' => 'themes.php', 308 'page_title' => esc_html__( 'One Click Demo Import' , 'pt-ocdi' ), 309 'menu_title' => esc_html__( 'Import Demo Data' , 'pt-ocdi' ), 310 'capability' => 'import', 311 'menu_slug' => 'pt-one-click-demo-import', 312 ) 313 ); 314 315 // Get user credentials for WP file-system API. 316 $demo_import_page_url = wp_nonce_url( $plugin_page_setup['parent_slug'] . '?page=' . $plugin_page_setup['menu_slug'], $plugin_page_setup['menu_slug'] ); 317 318 if ( false === ( $creds = request_filesystem_credentials( $demo_import_page_url, '', false, false, null ) ) ) { 319 return new \WP_error( 320 'filesystem_credentials_could_not_be_retrieved', 321 __( 'An error occurred while retrieving reading/writing permissions to your server (could not retrieve WP filesystem credentials)!', 'pt-ocdi' ) 322 ); 323 } 324 325 // Now we have credentials, try to get the wp_filesystem running. 326 if ( ! WP_Filesystem( $creds ) ) { 327 return new \WP_Error( 328 'wrong_login_credentials', 329 __( 'Your WordPress login credentials don\'t allow to use WP_Filesystem!', 'pt-ocdi' ) 330 ); 331 } 332 333 return true; 334 } 335 336 337 /** 338 * Get log file path 339 * 340 * @return string, path to the log file 341 */ 342 public static function get_log_path() { 343 $upload_dir = wp_upload_dir(); 344 $upload_path = apply_filters( 'pt-ocdi/upload_file_path', trailingslashit( $upload_dir['path'] ) ); 345 346 $log_path = $upload_path . apply_filters( 'pt-ocdi/log_file_prefix', 'log_file_' ) . self::$demo_import_start_time . apply_filters( 'pt-ocdi/log_file_suffix_and_file_extension', '.txt' ); 347 348 self::register_file_as_media_attachment( $log_path ); 349 350 return $log_path; 351 } 352 353 354 /** 355 * Register file as attachment to the Media page. 356 * 357 * @param string $log_path log file path. 358 * @return void 359 */ 360 public static function register_file_as_media_attachment( $log_path ) { 361 // Check the type of file. 362 $log_mimes = array( 'txt' => 'text/plain' ); 363 $filetype = wp_check_filetype( basename( $log_path ), apply_filters( 'pt-ocdi/file_mimes', $log_mimes ) ); 364 365 // Prepare an array of post data for the attachment. 366 $attachment = array( 367 'guid' => self::get_log_url( $log_path ), 368 'post_mime_type' => $filetype['type'], 369 'post_title' => apply_filters( 'pt-ocdi/attachment_prefix', esc_html__( 'One Click Demo Import - ', 'pt-ocdi' ) ) . preg_replace( '/\.[^.]+$/', '', basename( $log_path ) ), 370 'post_content' => '', 371 'post_status' => 'inherit', 372 ); 373 374 // Insert the file as attachment in Media page. 375 $attach_id = wp_insert_attachment( $attachment, $log_path ); 376 } 377 378 379 /** 380 * Get log file url 381 * 382 * @param string $log_path log path to use for the log filename. 383 * @return string, url to the log file. 384 */ 385 public static function get_log_url( $log_path ) { 386 $upload_dir = wp_upload_dir(); 387 $upload_url = apply_filters( 'pt-ocdi/upload_file_url', trailingslashit( $upload_dir['url'] ) ); 388 389 return $upload_url . basename( $log_path ); 390 } 391 392 393 /** 394 * Check if the AJAX call is valid. 395 */ 396 public static function verify_ajax_call() { 397 check_ajax_referer( 'ocdi-ajax-verification', 'security' ); 398 399 // Check if user has the WP capability to import data. 400 if ( ! current_user_can( 'import' ) ) { 401 wp_die( 402 sprintf( 403 __( '%sYour user role isn\'t high enough. You don\'t have permission to import demo data.%s', 'pt-ocdi' ), 404 '<div class="notice notice-error"><p>', 405 '</p></div>' 406 ) 407 ); 408 } 409 } 410 411 412 /** 413 * Process uploaded files and return the paths to these files. 414 * 415 * @param array $uploaded_files $_FILES array form an AJAX request. 416 * @param string $log_file_path path to the log file. 417 * @return array of paths to the content import and widget import files. 418 */ 419 public static function process_uploaded_files( $uploaded_files, $log_file_path ) { 420 // Variable holding the paths to the uploaded files. 421 $selected_import_files = array( 422 'content' => '', 423 'widgets' => '', 424 'customizer' => '', 425 'redux' => '', 426 ); 427 428 // Upload settings to disable form and type testing for AJAX uploads. 429 $upload_overrides = array( 430 'test_form' => false, 431 'test_type' => false, 432 ); 433 434 // Handle demo content and widgets file upload. 435 $content_file_info = wp_handle_upload( $_FILES['content_file'], $upload_overrides ); 436 $widget_file_info = wp_handle_upload( $_FILES['widget_file'], $upload_overrides ); 437 $customizer_file_info = wp_handle_upload( $_FILES['customizer_file'], $upload_overrides ); 438 $redux_file_info = wp_handle_upload( $_FILES['redux_file'], $upload_overrides ); 439 440 // Process content import file. 441 if ( $content_file_info && ! isset( $content_file_info['error'] ) ) { 442 // Set uploaded content file. 443 $selected_import_files['content'] = $content_file_info['file']; 444 } 445 else { 446 // Add this error to log file. 447 $log_added = self::append_to_file( 448 sprintf( 449 __( 'Content file was not uploaded. Error: %s', 'pt-ocdi' ), 450 $widget_file_info['error'] 451 ), 452 $log_file_path, 453 esc_html__( 'Upload files' , 'pt-ocdi' ) 454 ); 455 } 456 457 // Process widget import file. 458 if ( $widget_file_info && ! isset( $widget_file_info['error'] ) ) { 459 // Set uploaded widget file. 460 $selected_import_files['widgets'] = $widget_file_info['file']; 461 } 462 else { 463 // Add this error to log file. 464 $log_added = self::append_to_file( 465 sprintf( 466 __( 'Widget file was not uploaded. Error: %s', 'pt-ocdi' ), 467 $widget_file_info['error'] 468 ), 469 $log_file_path, 470 esc_html__( 'Upload files' , 'pt-ocdi' ) 471 ); 472 } 473 474 // Process Customizer import file. 475 if ( $customizer_file_info && ! isset( $customizer_file_info['error'] ) ) { 476 // Set uploaded customizer file. 477 $selected_import_files['customizer'] = $customizer_file_info['file']; 478 } 479 else { 480 // Add this error to log file. 481 $log_added = self::append_to_file( 482 sprintf( 483 __( 'Customizer file was not uploaded. Error: %s', 'pt-ocdi' ), 484 $customizer_file_info['error'] 485 ), 486 $log_file_path, 487 esc_html__( 'Upload files' , 'pt-ocdi' ) 488 ); 489 } 490 491 // Process Redux import file. 492 if ( $redux_file_info && ! isset( $redux_file_info['error'] ) ) { 493 if ( isset( $_POST['redux_option_name'] ) && empty( $_POST['redux_option_name'] ) ) { 494 // Write error to log file and send an AJAX response with the error. 495 self::log_error_and_send_ajax_response( 496 esc_html__( 'Missing Redux option name! Please also enter the Redux option name!', 'pt-ocdi' ), 497 $log_file_path, 498 esc_html__( 'Upload files', 'pt-ocdi' ) 499 ); 500 } 501 502 // Set uploaded Redux file. 503 $selected_import_files['redux'] = array( 504 array( 505 'option_name' => $_POST['redux_option_name'], 506 'file_path' => $redux_file_info['file'], 507 ), 508 ); 509 } 510 else { 511 // Add this error to log file. 512 $log_added = self::append_to_file( 513 sprintf( 514 __( 'Redux file was not uploaded. Error: %s', 'pt-ocdi' ), 515 $redux_file_info['error'] 516 ), 517 $log_file_path, 518 esc_html__( 'Upload files' , 'pt-ocdi' ) 519 ); 520 } 521 522 // Add this message to log file. 523 $log_added = self::append_to_file( 524 __( 'The import files were successfully uploaded!', 'pt-ocdi' ) . self::import_file_info( $selected_import_files ), 525 $log_file_path, 526 esc_html__( 'Upload files' , 'pt-ocdi' ) 527 ); 528 529 // Return array with paths of uploaded files. 530 return $selected_import_files; 531 } 532 533 534 /** 535 * Get import file information and max execution time. 536 * 537 * @param array $selected_import_files array of selected import files. 538 */ 539 public static function import_file_info( $selected_import_files ) { 540 $redux_file_string = ''; 541 542 if ( ! empty( $selected_import_files['redux'] ) ) { 543 $redux_file_string = array_reduce( $selected_import_files['redux'], function( $string, $item ) { 544 return sprintf( '%1$s%2$s -> %3$s %4$s', $string, $item['option_name'], $item['file_path'], PHP_EOL ); 545 }, '' ); 546 } 547 548 return PHP_EOL . 549 sprintf( 550 __( 'Initial max execution time = %s', 'pt-ocdi' ), 551 ini_get( 'max_execution_time' ) 552 ) . PHP_EOL . 553 sprintf( 554 __( 'Files info:%1$sSite URL = %2$s%1$sData file = %3$s%1$sWidget file = %4$s%1$sCustomizer file = %5$s%1$sRedux files:%1$s%6$s', 'pt-ocdi' ), 555 PHP_EOL, 556 get_site_url(), 557 empty( $selected_import_files['content'] ) ? esc_html__( 'not defined!', 'pt-ocdi' ) : $selected_import_files['content'], 558 empty( $selected_import_files['widgets'] ) ? esc_html__( 'not defined!', 'pt-ocdi' ) : $selected_import_files['widgets'], 559 empty( $selected_import_files['customizer'] ) ? esc_html__( 'not defined!', 'pt-ocdi' ) : $selected_import_files['customizer'], 560 empty( $redux_file_string ) ? esc_html__( 'not defined!', 'pt-ocdi' ) : $redux_file_string 561 ); 562 } 563 564 565 /** 566 * Write the error to the log file and send the AJAX response. 567 * 568 * @param string $error_text text to display in the log file and in the AJAX response. 569 * @param string $log_file_path path to the log file. 570 * @param string $separator title separating the old and new content. 571 */ 572 public static function log_error_and_send_ajax_response( $error_text, $log_file_path, $separator = '' ) { 573 // Add this error to log file. 574 $log_added = self::append_to_file( 575 $error_text, 576 $log_file_path, 577 $separator 578 ); 579 580 // Send JSON Error response to the AJAX call. 581 wp_send_json( $error_text ); 582 } 583 584 585 /** 586 * Set the $demo_import_start_time class variable with the current date and time string. 587 */ 588 public static function set_demo_import_start_time() { 589 self::$demo_import_start_time = date( apply_filters( 'pt-ocdi/date_format_for_file_names', 'Y-m-d__H-i-s' ) ); 590 } 591 592 593 /** 594 * Get the category list of all categories used in the predefined demo imports array. 595 * 596 * @param array $demo_imports Array of demo import items (arrays). 597 * @return array|boolean List of all the categories or false if there aren't any. 598 */ 599 public static function get_all_demo_import_categories( $demo_imports ) { 600 $categories = array(); 601 602 foreach ( $demo_imports as $item ) { 603 if ( ! empty( $item['categories'] ) && is_array( $item['categories'] ) ) { 604 foreach ( $item['categories'] as $category ) { 605 $categories[ sanitize_key( $category ) ] = $category; 606 } 607 } 608 } 609 610 if ( empty( $categories ) ) { 611 return false; 612 } 613 614 return $categories; 615 } 616 617 618 /** 619 * Return the concatenated string of demo import item categories. 620 * These should be separated by comma and sanitized properly. 621 * 622 * @param array $item The predefined demo import item data. 623 * @return string The concatenated string of categories. 624 */ 625 public static function get_demo_import_item_categories( $item ) { 626 $sanitized_categories = array(); 627 628 if ( isset( $item['categories'] ) ) { 629 foreach ( $item['categories'] as $category ) { 630 $sanitized_categories[] = sanitize_key( $category ); 631 } 632 } 633 634 if ( ! empty( $sanitized_categories ) ) { 635 return implode( ',', $sanitized_categories ); 636 } 637 638 return false; 639 } 640 641 642 /** 643 * Set the OCDI transient with the current importer data. 644 * 645 * @param array $data Data to be saved to the transient. 646 */ 647 public static function set_ocdi_import_data_transient( $data ) { 648 set_transient( 'ocdi_importer_data', $data, 0.1 * HOUR_IN_SECONDS ); 649 } 650 }