OneClickDemoImport.php (18725B)
1 <?php 2 3 /** 4 * Main One Click Demo Import plugin class/file. 5 * 6 * @package ocdi 7 */ 8 9 namespace OCDI; 10 11 /** 12 * One Click Demo Import class, so we don't have to worry about namespaces. 13 */ 14 class OneClickDemoImport { 15 16 /** 17 * The instance *Singleton* of this class 18 * 19 * @var object 20 */ 21 private static $instance; 22 23 /** 24 * The instance of the OCDI\Importer class. 25 * 26 * @var object 27 */ 28 public $importer; 29 30 /** 31 * The resulting page's hook_suffix, or false if the user does not have the capability required. 32 * 33 * @var boolean or string 34 */ 35 private $plugin_page; 36 37 /** 38 * Holds the verified import files. 39 * 40 * @var array 41 */ 42 public $import_files; 43 44 /** 45 * The path of the log file. 46 * 47 * @var string 48 */ 49 public $log_file_path; 50 51 /** 52 * The index of the `import_files` array (which import files was selected). 53 * 54 * @var int 55 */ 56 private $selected_index; 57 58 /** 59 * The paths of the actual import files to be used in the import. 60 * 61 * @var array 62 */ 63 private $selected_import_files; 64 65 /** 66 * Holds any error messages, that should be printed out at the end of the import. 67 * 68 * @var string 69 */ 70 public $frontend_error_messages = array(); 71 72 /** 73 * Was the before content import already triggered? 74 * 75 * @var boolean 76 */ 77 private $before_import_executed = false; 78 79 /** 80 * Make plugin page options available to other methods. 81 * 82 * @var array 83 */ 84 private $plugin_page_setup = array(); 85 86 /** 87 * Returns the *Singleton* instance of this class. 88 * 89 * @return OneClickDemoImport the *Singleton* instance. 90 */ 91 public static function get_instance() { 92 if ( null === static::$instance ) { 93 static::$instance = new static(); 94 } 95 96 return static::$instance; 97 } 98 99 100 /** 101 * Class construct function, to initiate the plugin. 102 * Protected constructor to prevent creating a new instance of the 103 * *Singleton* via the `new` operator from outside of this class. 104 */ 105 protected function __construct() { 106 // Actions. 107 add_action( 'admin_menu', array( $this, 'create_plugin_page' ) ); 108 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); 109 add_action( 'wp_ajax_ocdi_import_demo_data', array( $this, 'import_demo_data_ajax_callback' ) ); 110 add_action( 'wp_ajax_ocdi_import_customizer_data', array( $this, 'import_customizer_data_ajax_callback' ) ); 111 add_action( 'wp_ajax_ocdi_after_import_data', array( $this, 'after_all_import_data_ajax_callback' ) ); 112 add_action( 'after_setup_theme', array( $this, 'setup_plugin_with_filter_data' ) ); 113 add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) ); 114 } 115 116 117 /** 118 * Private clone method to prevent cloning of the instance of the *Singleton* instance. 119 * 120 * @return void 121 */ 122 private function __clone() { } 123 124 125 /** 126 * Private unserialize method to prevent unserializing of the *Singleton* instance. 127 * 128 * @return void 129 */ 130 public function __wakeup() { } 131 132 133 /** 134 * Creates the plugin page and a submenu item in WP Appearance menu. 135 */ 136 public function create_plugin_page() { 137 $this->plugin_page_setup = apply_filters( 138 'pt-ocdi/plugin_page_setup', 139 array( 140 'parent_slug' => 'themes.php', 141 'page_title' => esc_html__( 'One Click Demo Import', 'pt-ocdi' ), 142 'menu_title' => esc_html__( 'Import Demo Data', 'pt-ocdi' ), 143 'capability' => 'import', 144 'menu_slug' => 'pt-one-click-demo-import', 145 ) 146 ); 147 148 $this->plugin_page = add_submenu_page( 149 $this->plugin_page_setup['parent_slug'], 150 $this->plugin_page_setup['page_title'], 151 $this->plugin_page_setup['menu_title'], 152 $this->plugin_page_setup['capability'], 153 $this->plugin_page_setup['menu_slug'], 154 apply_filters( 'pt-ocdi/plugin_page_display_callback_function', array( $this, 'display_plugin_page' ) ) 155 ); 156 157 register_importer( $this->plugin_page_setup['menu_slug'], $this->plugin_page_setup['page_title'], $this->plugin_page_setup['menu_title'], apply_filters( 'pt-ocdi/plugin_page_display_callback_function', array( $this, 'display_plugin_page' ) ) ); 158 } 159 160 161 /** 162 * Plugin page display. 163 * Output (HTML) is in another file. 164 */ 165 public function display_plugin_page() { 166 require_once PT_OCDI_PATH . 'views/plugin-page.php'; 167 } 168 169 170 /** 171 * Enqueue admin scripts (JS and CSS) 172 * 173 * @param string $hook holds info on which admin page you are currently loading. 174 */ 175 public function admin_enqueue_scripts( $hook ) { 176 // Enqueue the scripts only on the plugin page. 177 if ( $this->plugin_page === $hook || ( 'admin.php' === $hook && $this->plugin_page_setup['menu_slug'] === esc_attr( $_GET['import'] ) ) ) { 178 wp_enqueue_script( 'jquery-ui-dialog' ); 179 wp_enqueue_style( 'wp-jquery-ui-dialog' ); 180 181 wp_enqueue_script( 'ocdi-main-js', PT_OCDI_URL . 'assets/js/main.js', array( 'jquery', 'jquery-ui-dialog' ), PT_OCDI_VERSION ); 182 183 // Get theme data. 184 $theme = wp_get_theme(); 185 186 wp_localize_script( 187 'ocdi-main-js', 188 'ocdi', 189 array( 190 'ajax_url' => admin_url( 'admin-ajax.php' ), 191 'ajax_nonce' => wp_create_nonce( 'ocdi-ajax-verification' ), 192 'import_files' => $this->import_files, 193 'wp_customize_on' => apply_filters( 'pt-ocdi/enable_wp_customize_save_hooks', false ), 194 'import_popup' => apply_filters( 'pt-ocdi/enable_grid_layout_import_popup_confirmation', true ), 195 'theme_screenshot' => $theme->get_screenshot(), 196 'texts' => array( 197 'missing_preview_image' => esc_html__( 'No preview image defined for this import.', 'pt-ocdi' ), 198 'dialog_title' => esc_html__( 'Are you sure?', 'pt-ocdi' ), 199 'dialog_no' => esc_html__( 'Cancel', 'pt-ocdi' ), 200 'dialog_yes' => esc_html__( 'Yes, import!', 'pt-ocdi' ), 201 'selected_import_title' => esc_html__( 'Selected demo import:', 'pt-ocdi' ), 202 ), 203 'dialog_options' => apply_filters( 'pt-ocdi/confirmation_dialog_options', array() ), 204 ) 205 ); 206 207 wp_enqueue_style( 'ocdi-main-css', PT_OCDI_URL . 'assets/css/main.css', array(), PT_OCDI_VERSION ); 208 } 209 } 210 211 212 /** 213 * Main AJAX callback function for: 214 * 1). prepare import files (uploaded or predefined via filters) 215 * 2). execute 'before content import' actions (before import WP action) 216 * 3). import content 217 * 4). execute 'after content import' actions (before widget import WP action, widget import, customizer import, after import WP action) 218 */ 219 public function import_demo_data_ajax_callback() { 220 // Try to update PHP memory limit (so that it does not run out of it). 221 ini_set( 'memory_limit', apply_filters( 'pt-ocdi/import_memory_limit', '350M' ) ); 222 223 // Verify if the AJAX call is valid (checks nonce and current_user_can). 224 Helpers::verify_ajax_call(); 225 226 // Is this a new AJAX call to continue the previous import? 227 $use_existing_importer_data = $this->use_existing_importer_data(); 228 229 if ( ! $use_existing_importer_data ) { 230 // Create a date and time string to use for demo and log file names. 231 Helpers::set_demo_import_start_time(); 232 233 // Define log file path. 234 $this->log_file_path = Helpers::get_log_path(); 235 236 // Get selected file index or set it to 0. 237 $this->selected_index = empty( $_POST['selected'] ) ? 0 : absint( $_POST['selected'] ); 238 /** 239 * 1). Prepare import files. 240 * Manually uploaded import files or predefined import files via filter: pt-ocdi/import_files 241 */ 242 if ( ! empty( $_FILES ) ) { // Using manual file uploads? 243 // Get paths for the uploaded files. 244 $this->selected_import_files = Helpers::process_uploaded_files( $_FILES, $this->log_file_path ); 245 246 // Set the name of the import files, because we used the uploaded files. 247 $this->import_files[ $this->selected_index ]['import_file_name'] = esc_html__( 'Manually uploaded files', 'pt-ocdi' ); 248 } elseif ( ! empty( $this->import_files[ $this->selected_index ] ) ) { // Use predefined import files from wp filter: pt-ocdi/import_files. 249 250 // Download the import files (content, widgets and customizer files). 251 $this->selected_import_files = Helpers::download_import_files( $this->import_files[ $this->selected_index ] ); 252 253 // Check Errors. 254 if ( is_wp_error( $this->selected_import_files ) ) { 255 // Write error to log file and send an AJAX response with the error. 256 Helpers::log_error_and_send_ajax_response( 257 $this->selected_import_files->get_error_message(), 258 $this->log_file_path, 259 esc_html__( 'Downloaded files', 'pt-ocdi' ) 260 ); 261 } 262 263 // Add this message to log file. 264 $log_added = Helpers::append_to_file( 265 sprintf( 266 __( 'The import files for: %s were successfully downloaded!', 'pt-ocdi' ), 267 $this->import_files[ $this->selected_index ]['import_file_name'] 268 ) . Helpers::import_file_info( $this->selected_import_files ), 269 $this->log_file_path, 270 esc_html__( 'Downloaded files', 'pt-ocdi' ) 271 ); 272 } else { 273 // Send JSON Error response to the AJAX call. 274 wp_send_json( esc_html__( 'No import files specified!', 'pt-ocdi' ) ); 275 } 276 } 277 278 // Save the initial import data as a transient, so other import parts (in new AJAX calls) can use that data. 279 Helpers::set_ocdi_import_data_transient( $this->get_current_importer_data() ); 280 281 if ( ! $this->before_import_executed ) { 282 $this->before_import_executed = true; 283 284 /** 285 * 2). Execute the actions hooked to the 'pt-ocdi/before_content_import_execution' action: 286 * 287 * Default actions: 288 * 1 - Before content import WP action (with priority 10). 289 */ 290 do_action( 'pt-ocdi/before_content_import_execution', $this->selected_import_files, $this->import_files, $this->selected_index ); 291 } 292 293 /** 294 * 3). Import content (if the content XML file is set for this import). 295 * Returns any errors greater then the "warning" logger level, that will be displayed on front page. 296 */ 297 if ( ! empty( $this->selected_import_files['content'] ) ) { 298 $this->append_to_frontend_error_messages( $this->importer->import_content( $this->selected_import_files['content'] ) ); 299 } 300 301 /** 302 * 4). Execute the actions hooked to the 'pt-ocdi/after_content_import_execution' action: 303 * 304 * Default actions: 305 * 1 - Before widgets import setup (with priority 10). 306 * 2 - Import widgets (with priority 20). 307 * 3 - Import Redux data (with priority 30). 308 */ 309 do_action( 'pt-ocdi/after_content_import_execution', $this->selected_import_files, $this->import_files, $this->selected_index ); 310 311 // Save the import data as a transient, so other import parts (in new AJAX calls) can use that data. 312 Helpers::set_ocdi_import_data_transient( $this->get_current_importer_data() ); 313 314 // Request the customizer import AJAX call. 315 if ( ! empty( $this->selected_import_files['customizer'] ) ) { 316 wp_send_json( array( 'status' => 'customizerAJAX' ) ); 317 } 318 319 // Request the after all import AJAX call. 320 if ( false !== has_action( 'pt-ocdi/after_all_import_execution' ) ) { 321 wp_send_json( array( 'status' => 'afterAllImportAJAX' ) ); 322 } 323 324 // Send a JSON response with final report. 325 $this->final_response(); 326 } 327 328 329 /** 330 * AJAX callback for importing the customizer data. 331 * This request has the wp_customize set to 'on', so that the customizer hooks can be called 332 * (they can only be called with the $wp_customize instance). But if the $wp_customize is defined, 333 * then the widgets do not import correctly, that's why the customizer import has its own AJAX call. 334 */ 335 public function import_customizer_data_ajax_callback() { 336 // Verify if the AJAX call is valid (checks nonce and current_user_can). 337 Helpers::verify_ajax_call(); 338 339 // Get existing import data. 340 if ( $this->use_existing_importer_data() ) { 341 /** 342 * Execute the customizer import actions. 343 * 344 * Default actions: 345 * 1 - Customizer import (with priority 10). 346 */ 347 do_action( 'pt-ocdi/customizer_import_execution', $this->selected_import_files ); 348 } 349 350 // Request the after all import AJAX call. 351 if ( false !== has_action( 'pt-ocdi/after_all_import_execution' ) ) { 352 wp_send_json( array( 'status' => 'afterAllImportAJAX' ) ); 353 } 354 355 // Send a JSON response with final report. 356 $this->final_response(); 357 } 358 359 360 /** 361 * AJAX callback for the after all import action. 362 */ 363 public function after_all_import_data_ajax_callback() { 364 // Verify if the AJAX call is valid (checks nonce and current_user_can). 365 Helpers::verify_ajax_call(); 366 367 // Get existing import data. 368 if ( $this->use_existing_importer_data() ) { 369 /** 370 * Execute the after all import actions. 371 * 372 * Default actions: 373 * 1 - after_import action (with priority 10). 374 */ 375 do_action( 'pt-ocdi/after_all_import_execution', $this->selected_import_files, $this->import_files, $this->selected_index ); 376 } 377 378 // Send a JSON response with final report. 379 $this->final_response(); 380 } 381 382 383 /** 384 * Send a JSON response with final report. 385 */ 386 private function final_response() { 387 // Delete importer data transient for current import. 388 delete_transient( 'ocdi_importer_data' ); 389 390 // Display final messages (success or error messages). 391 if ( empty( $this->frontend_error_messages ) ) { 392 $response['message'] = ''; 393 394 if ( ! apply_filters( 'pt-ocdi/disable_pt_branding', false ) ) { 395 $twitter_status = esc_html__( 'Just used One Click Demo Import plugin and it was awesome! Thanks @ProteusThemes! #OCDI https://www.proteusthemes.com/', 'pt-ocdi' ); 396 397 $response['message'] .= sprintf( 398 __( '%1$s%6$sWasn\'t this a great One Click Demo Import experience?%7$s Created and maintained by %3$sProteusThemes%4$s. %2$s%5$sClick to Tweet!%4$s%8$s', 'pt-ocdi' ), 399 '<div class="notice notice-info"><p>', 400 '<br>', 401 '<strong><a href="https://www.proteusthemes.com/" target="_blank">', 402 '</a></strong>', 403 '<strong><a href="' . add_query_arg( 'status', urlencode( $twitter_status ), 'http://twitter.com/home' ) . '" target="_blank">', 404 '<strong>', 405 '</strong>', 406 '</p></div>' 407 ); 408 } 409 410 $response['message'] .= sprintf( 411 __( '%1$s%3$sThat\'s it, all done!%4$s%2$sThe demo import has finished. Please check your page and make sure that everything has imported correctly. If it did, you can deactivate the %3$sDemo Installer%4$s plugin, because it has done its job.%5$s', 'pt-ocdi' ), 412 '<div class="notice notice-success"><p>', 413 '<br>', 414 '<strong>', 415 '</strong>', 416 '</p></div>' 417 ); 418 } else { 419 $response['message'] = $this->frontend_error_messages_display() . '<br>'; 420 $response['message'] .= sprintf( 421 __( '%1$sThe demo import has finished, but there were some import errors.%2$sMore details about the errors can be found in this %3$s%5$slog file%6$s%4$s%7$s', 'pt-ocdi' ), 422 '<div class="notice notice-warning"><p>', 423 '<br>', 424 '<strong>', 425 '</strong>', 426 '<a href="' . Helpers::get_log_url( $this->log_file_path ) . '" target="_blank">', 427 '</a>', 428 '</p></div>' 429 ); 430 } 431 432 wp_send_json( $response ); 433 } 434 435 436 /** 437 * Get content importer data, so we can continue the import with this new AJAX request. 438 * 439 * @return boolean 440 */ 441 private function use_existing_importer_data() { 442 if ( $data = get_transient( 'ocdi_importer_data' ) ) { 443 $this->frontend_error_messages = empty( $data['frontend_error_messages'] ) ? array() : $data['frontend_error_messages']; 444 $this->log_file_path = empty( $data['log_file_path'] ) ? '' : $data['log_file_path']; 445 $this->selected_index = empty( $data['selected_index'] ) ? 0 : $data['selected_index']; 446 $this->selected_import_files = empty( $data['selected_import_files'] ) ? array() : $data['selected_import_files']; 447 $this->before_import_executed = empty( $data['before_import_executed'] ) ? false : $data['before_import_executed']; 448 $this->importer->set_importer_data( $data ); 449 450 return true; 451 } 452 return false; 453 } 454 455 456 /** 457 * Get the current state of selected data. 458 * 459 * @return array 460 */ 461 public function get_current_importer_data() { 462 return array( 463 'frontend_error_messages' => $this->frontend_error_messages, 464 'log_file_path' => $this->log_file_path, 465 'selected_index' => $this->selected_index, 466 'selected_import_files' => $this->selected_import_files, 467 'before_import_executed' => $this->before_import_executed, 468 ); 469 } 470 471 472 /** 473 * Getter function to retrieve the private log_file_path value. 474 * 475 * @return string The log_file_path value. 476 */ 477 public function get_log_file_path() { 478 return $this->log_file_path; 479 } 480 481 482 /** 483 * Setter function to append additional value to the private frontend_error_messages value. 484 * 485 * @param string $additional_value The additional value that will be appended to the existing frontend_error_messages. 486 */ 487 public function append_to_frontend_error_messages( $text ) { 488 $lines = array(); 489 490 if ( ! empty( $text ) ) { 491 $text = str_replace( '<br>', PHP_EOL, $text ); 492 $lines = explode( PHP_EOL, $text ); 493 } 494 495 foreach ( $lines as $line ) { 496 if ( ! empty( $line ) && ! in_array( $line, $this->frontend_error_messages ) ) { 497 $this->frontend_error_messages[] = $line; 498 } 499 } 500 } 501 502 503 /** 504 * Display the frontend error messages. 505 * 506 * @return string Text with HTML markup. 507 */ 508 public function frontend_error_messages_display() { 509 $output = ''; 510 511 if ( ! empty( $this->frontend_error_messages ) ) { 512 foreach ( $this->frontend_error_messages as $line ) { 513 $output .= esc_html( $line ); 514 $output .= '<br>'; 515 } 516 } 517 518 return $output; 519 } 520 521 522 /** 523 * Load the plugin textdomain, so that translations can be made. 524 */ 525 public function load_textdomain() { 526 load_plugin_textdomain( 'pt-ocdi', false, plugin_basename( dirname( dirname( __FILE__ ) ) ) . '/languages' ); 527 } 528 529 530 /** 531 * Get data from filters, after the theme has loaded and instantiate the importer. 532 */ 533 public function setup_plugin_with_filter_data() { 534 // Get info of import data files and filter it. 535 $this->import_files = Helpers::validate_import_file_info( apply_filters( 'pt-ocdi/import_files', array() ) ); 536 537 /** 538 * Register all default actions (before content import, widget, customizer import and other actions) 539 * to the 'before_content_import_execution' and the 'pt-ocdi/after_content_import_execution' action hook. 540 */ 541 $import_actions = new ImportActions(); 542 $import_actions->register_hooks(); 543 544 // Importer options array. 545 $importer_options = apply_filters( 546 'pt-ocdi/importer_options', 547 array( 548 'fetch_attachments' => true, 549 ) 550 ); 551 552 // Logger options for the logger used in the importer. 553 $logger_options = apply_filters( 554 'pt-ocdi/logger_options', 555 array( 556 'logger_min_level' => 'warning', 557 ) 558 ); 559 560 // Configure logger instance and set it to the importer. 561 $logger = new Logger(); 562 $logger->min_level = $logger_options['logger_min_level']; 563 564 // Create importer instance with proper parameters. 565 $this->importer = new Importer( $importer_options, $logger ); 566 } 567 }