plugin_name = $plugin_name; $this->version = $version; $this->faq_url = 'https://wordpress.org/plugins/fg-spip-to-wp/faq/'; $upload_dir = wp_upload_dir(); $this->log_file = $upload_dir['basedir'] . '/' . $this->plugin_name . '.logs'; $this->log_file_url = $upload_dir['baseurl'] . '/' . $this->plugin_name . '.logs'; // Replace the protocol if the WordPress address is wrong in the WordPress General settings if ( is_ssl() ) { $this->log_file_url = preg_replace('/^https?/', 'https', $this->log_file_url); } // Progress bar $this->progressbar = new FG_Spip_to_WordPress_ProgressBar($this); } /** * The name of the plugin used to uniquely identify it within the context of * WordPress and to define internationalization functionality. * * @since 1.0.0 * @return string The name of the plugin. */ public function get_plugin_name() { return $this->plugin_name; } /** * Register the stylesheets for the admin area. * * @since 1.0.0 */ public function enqueue_styles() { wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/fg-spip-to-wp-admin.css', array(), $this->version, 'all' ); } /** * Register the JavaScript for the admin area. * * @since 1.0.0 */ public function enqueue_scripts() { wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/fg-spip-to-wp-admin.js', array( 'jquery', 'jquery-ui-progressbar' ), $this->version, false ); wp_localize_script( $this->plugin_name, 'objectL10n', array( 'delete_imported_data_confirmation_message' => __( 'All previously imported data will be deleted from WordPress.', 'fg-spip-to-wp' ), 'delete_all_confirmation_message' => __( 'All content will be deleted from WordPress.', 'fg-spip-to-wp' ), 'delete_no_answer_message' => __( 'Please select a remove option.', 'fg-spip-to-wp' ), 'import_completed' => __( 'IMPORT COMPLETED', 'fg-spip-to-wp' ), 'content_removed_from_wordpress' => __( 'Content removed from WordPress', 'fg-spip-to-wp' ), 'settings_saved' => __( 'Settings saved', 'fg-spip-to-wp' ), 'importing' => __( 'Importing…', 'fg-spip-to-wp' ), 'import_stopped_by_user' => __( 'IMPORT STOPPED BY USER', 'fg-spip-to-wp' ), 'internal_links_modified' => __( 'Internal links modified', 'fg-spip-to-wp' ), ) ); wp_localize_script( $this->plugin_name, 'objectPlugin', array( 'log_file_url' => $this->log_file_url, 'progress_url' => $this->progressbar->get_url(), )); } /** * Initialize the plugin */ public function init() { register_importer('fg-spip-to-wp', __('SPIP', 'fg-spip-to-wp'), __('Import categories, articles, news and images from a SPIP database into WordPress', 'fg-spip-to-wp'), array($this, 'importer')); } /** * Display the stored notices * * @since 2.0.0 */ public function display_notices() { foreach ( $this->notices as $notice ) { echo '

[' . $this->plugin_name . '] ' . $notice['message'] . "

\n"; } } /** * Write a message in the log file * * @since 2.0.0 * * @param string $message */ public function log($message) { file_put_contents($this->log_file, "$message\n", FILE_APPEND); } /** * Store an admin notice */ public function display_admin_notice( $message ) { $this->notices[] = array('level' => 'updated', 'message' => $message); error_log('[INFO] [' . $this->plugin_name . '] ' . $message); $this->log($message); } /** * Store an admin error */ public function display_admin_error( $message ) { $this->notices[] = array('level' => 'error', 'message' => $message); error_log('[ERROR] [' . $this->plugin_name . '] ' . $message); $this->log('[ERROR] ' . $message); } /** * Store an admin warning */ public function display_admin_warning( $message ) { $this->notices[] = array('level' => 'error', 'message' => $message); error_log('[WARNING] [' . $this->plugin_name . '] ' . $message); $this->log('[WARNING] ' . $message); } /** * Run the importer * * @since 2.0.0 */ public function importer() { $feasible_actions = array( 'empty', 'save', 'test_database', 'import', 'modify_links', ); $action = ''; foreach ( $feasible_actions as $potential_action ) { if ( isset($_POST[$potential_action]) ) { $action = $potential_action; break; } } $this->dispatch($action); $this->display_admin_page(); // Display the admin page } /** * Import triggered by AJAX * * @since 2.0.0 */ public function ajax_importer() { $current_user = wp_get_current_user(); if ( !empty($current_user) && $current_user->has_cap('import') ) { $action = filter_input(INPUT_POST, 'plugin_action', FILTER_SANITIZE_STRING); if ( $action == 'update_wordpress_info') { // Update the WordPress database info echo $this->get_database_info(); } else { ini_set('display_errors', true); // Display the errors that may happen (ex: Allowed memory size exhausted) // Empty the log file if we empty the WordPress content if ( ($action == 'empty') || (($action == 'import') && filter_input(INPUT_POST, 'automatic_empty', FILTER_VALIDATE_BOOLEAN)) ) { file_put_contents($this->log_file, ''); } $time_start = date('Y-m-d H:i:s'); $this->display_admin_notice("=== START $action $time_start ==="); $result = $this->dispatch($action); if ( !empty($result) ) { echo json_encode($result); // Send the result to the AJAX caller } $time_end = date('Y-m-d H:i:s'); $this->display_admin_notice("=== END $action $time_end ===\n"); } } wp_die(); } /** * Dispatch the actions * * @param string $action Action * @return object Result to return to the caller */ public function dispatch($action) { set_time_limit(self::IMPORT_TIMEOUT); // Set the time zone $timezone = get_option('timezone_string'); if ( !empty($timezone) ) { date_default_timezone_set($timezone); } // Suspend the cache during the migration to avoid exhausted memory problem wp_suspend_cache_addition(true); wp_suspend_cache_invalidation(true); // Default values $this->plugin_options = array( 'automatic_empty' => 0, 'driver' => 'mysql', 'hostname' => 'localhost', 'port' => 3306, 'database' => null, 'username' => 'root', 'password' => '', 'sqlite_file' => '', 'prefix' => 'spip_', 'introtext' => 'in_content', 'archived_posts' => 'not_imported', 'skip_media' => 0, 'media_import_method' => 'http', 'url' => null, 'root_directory' => null, 'logo' => 'as_featured', 'import_external' => 0, 'import_duplicates' => 0, 'force_media_import' => 0, 'import_as_pages' => 0, 'timeout' => 5, 'logger_autorefresh' => 1, ); $options = get_option('fgs2wp_options'); if ( is_array($options) ) { $this->plugin_options = array_merge($this->plugin_options, $options); } // Check if the upload directory is writable $upload_dir = wp_upload_dir(); if ( !is_writable($upload_dir['basedir']) ) { $this->display_admin_error(__('The wp-content directory must be writable.', 'fg-spip-to-wp')); } // Requires at least WordPress 4.4 if ( version_compare(get_bloginfo('version'), '4.4', '<') ) { $this->display_admin_error(sprintf(__('WordPress 4.4+ is required. Please update WordPress.', 'fg-spip-to-wp'), admin_url('update-core.php'))); } elseif ( !empty($action) ) { switch($action) { // Delete content case 'empty': if ( check_admin_referer( 'empty', 'fgs2wp_nonce' ) ) { // Security check if ($this->empty_database($_POST['empty_action'])) { // Empty WP database $this->display_admin_notice(__('WordPress content removed', 'fg-spip-to-wp')); } else { $this->display_admin_error(__('Couldn\'t remove content', 'fg-spip-to-wp')); } wp_cache_flush(); } break; // Save database options case 'save': if ( check_admin_referer( 'parameters_form', 'fgs2wp_nonce' ) ) { // Security check $this->save_plugin_options(); $this->display_admin_notice(__('Settings saved', 'fg-spip-to-wp')); } break; // Test the database connection case 'test_database': if ( check_admin_referer( 'parameters_form', 'fgs2wp_nonce' ) ) { // Security check // Save database options $this->save_plugin_options(); if ( $this->test_database_connection() ) { return array('status' => 'OK', 'message' => __('Connection successful', 'fg-spip-to-wp')); } else { return array('status' => 'Error', 'message' => __('Connection failed', 'fg-spip-to-wp') . '
' . __('See the errors in the log below', 'fg-spip-to-wp')); } } break; // Run the import case 'import': if ( check_admin_referer( 'parameters_form', 'fgs2wp_nonce' ) ) { // Security check // Save database options $this->save_plugin_options(); if ( $this->test_database_connection() ) { // Automatic empty if ( $this->plugin_options['automatic_empty'] ) { if ($this->empty_database('all')) { $this->display_admin_notice(__('WordPress content removed', 'fg-spip-to-wp')); } else { $this->display_admin_error(__('Couldn\'t remove content', 'fg-spip-to-wp')); } wp_cache_flush(); } // Import content $this->import(); } } break; // Stop the import case 'stop_import': if ( check_admin_referer( 'parameters_form', 'fgs2wp_nonce' ) ) { // Security check $this->stop_import(); } break; // Modify internal links case 'modify_links': if ( check_admin_referer( 'modify_links', 'fgs2wp_nonce' ) ) { // Security check $result = $this->modify_links(); $this->display_admin_notice(sprintf(_n('%d internal link modified', '%d internal links modified', $result['links_count'], 'fg-spip-to-wp'), $result['links_count'])); } break; default: // Do other actions do_action('fgs2wp_dispatch', $action); } } } /** * Display the admin page * */ private function display_admin_page() { $data = $this->plugin_options; $data['title'] = __('Import SPIP', 'fg-spip-to-wp'); $data['description'] = __('This plugin will import categories, articles, news and images from a SPIP database into WordPress.
Compatible with SPIP from 1.8 to 3.2.', 'fg-spip-to-wp'); $data['description'] .= "
\n" . sprintf(__('For any issue, please read the FAQ first.', 'fg-spip-to-wp'), $this->faq_url); $data['database_info'] = $this->get_database_info(); // Hook for modifying the admin page $data = apply_filters('fgs2wp_pre_display_admin_page', $data); // Load the CSS and Javascript $this->enqueue_styles(); $this->enqueue_scripts(); include plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/admin-display.php'; // Hook for doing other actions after displaying the admin page do_action('fgs2wp_post_display_admin_page'); } /** * Get the WordPress database info * * @return string Database info */ private function get_database_info() { $posts_count = $this->count_posts('post'); $pages_count = $this->count_posts('page'); $media_count = $this->count_posts('attachment'); $cat_count = wp_count_terms('category', array('hide_empty' => 0)); $database_info = sprintf(_n('%d category', '%d categories', $cat_count, 'fg-spip-to-wp'), $cat_count) . "
" . sprintf(_n('%d post', '%d posts', $posts_count, 'fg-spip-to-wp'), $posts_count) . "
" . sprintf(_n('%d page', '%d pages', $pages_count, 'fg-spip-to-wp'), $pages_count) . "
" . sprintf(_n('%d media', '%d medias', $media_count, 'fg-spip-to-wp'), $media_count) . "
"; $database_info = apply_filters('fgs2wp_get_database_info', $database_info); return $database_info; } /** * Count the number of posts for a post type * @param string $post_type */ public function count_posts($post_type) { $count = 0; $excluded_status = array('trash', 'auto-draft'); $tab_count = wp_count_posts($post_type); foreach ( $tab_count as $key => $value ) { if ( !in_array($key, $excluded_status) ) { $count += $value; } } return $count; } /** * Add an help tab * */ public function add_help_tab() { $screen = get_current_screen(); $screen->add_help_tab(array( 'id' => 'fgs2wp_help_instructions', 'title' => __('Instructions', 'fg-spip-to-wp'), 'content' => '', 'callback' => array($this, 'help_instructions'), )); $screen->add_help_tab(array( 'id' => 'fgs2wp_help_options', 'title' => __('Options', 'fg-spip-to-wp'), 'content' => '', 'callback' => array($this, 'help_options'), )); $screen->set_help_sidebar('' . __('FAQ', 'fg-spip-to-wp') . ''); } /** * Instructions help screen * * @return string Help content */ public function help_instructions() { include plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/help-instructions.tpl.php'; } /** * Options help screen * * @return string Help content */ public function help_options() { include plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/help-options.tpl.php'; } /** * Open the connection on Spip database * * return boolean Connection successful or not */ protected function spip_connect() { global $spip_db; if ( !class_exists('PDO') ) { $this->display_admin_error(__('PDO is required. Please enable it.', 'fg-spip-to-wp')); return false; } try { switch ( $this->plugin_options['driver'] ) { case 'mysql': // MySQL $spip_db = new PDO('mysql:host=' . $this->plugin_options['hostname'] . ';port=' . $this->plugin_options['port'] . ';dbname=' . $this->plugin_options['database'], $this->plugin_options['username'], $this->plugin_options['password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'')); break; case 'sqlite': // SQLite if ( !file_exists($this->plugin_options['sqlite_file']) ) { $this->display_admin_error(__("Couldn't read the SPIP database SQLite file: ", 'fg-spip-to-wp') . $this->plugin_options['sqlite_file']); return false; } $spip_db = new PDO('sqlite:' . $this->plugin_options['sqlite_file']); break; } if ( defined('WP_DEBUG') && WP_DEBUG ) { $spip_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Display SQL errors } } catch ( PDOException $e ) { $this->display_admin_error(__('Couldn\'t connect to the SPIP database. Please check your parameters. And be sure the WordPress server can access the SPIP database.', 'fg-spip-to-wp') . "
\n" . $e->getMessage() . "
\n" . sprintf(__('Please read the FAQ for the solution.', 'fg-spip-to-wp'), $this->faq_url)); return false; } $this->spip_version = $this->get_spip_version(); $this->spip_charset = $this->get_spip_meta('charset'); $this->blob_encoding = $this->is_text_encoded_as_blob() ; return true; } /** * Execute a SQL query on the Spip database * * @param string $sql SQL query * @return array Query result */ public function spip_query($sql) { global $spip_db; $result = array(); try { $query = $spip_db->query($sql, PDO::FETCH_ASSOC); if ( is_object($query) ) { foreach ( $query as $row ) { $result[] = $row; } } } catch ( PDOException $e ) { $this->display_admin_error(__('Error:', 'fg-spip-to-wp') . $e->getMessage()); } return $result; } /** * Delete all posts, medias and categories from the database * * @param string $action imported = removes only new imported posts * all = removes all * @return boolean */ private function empty_database($action) { global $wpdb; $result = true; $wpdb->show_errors(); // Hook for doing other actions before emptying the database do_action('fgs2wp_pre_empty_database', $action); $sql_queries = array(); if ( $action == 'all' ) { // Remove all content $sql_queries[] = "TRUNCATE $wpdb->commentmeta"; $sql_queries[] = "TRUNCATE $wpdb->comments"; $sql_queries[] = "TRUNCATE $wpdb->term_relationships"; $sql_queries[] = "TRUNCATE $wpdb->termmeta"; $sql_queries[] = "TRUNCATE $wpdb->postmeta"; $sql_queries[] = "TRUNCATE $wpdb->posts"; $sql_queries[] = <<terms WHERE term_id > 1 -- non-classe SQL; $sql_queries[] = <<term_taxonomy WHERE term_id > 1 -- non-classe SQL; $sql_queries[] = "ALTER TABLE $wpdb->terms AUTO_INCREMENT = 2"; $sql_queries[] = "ALTER TABLE $wpdb->term_taxonomy AUTO_INCREMENT = 2"; } else { // (Re)create a temporary table with the IDs to delete $sql_queries[] = <<prefix}fg_data_to_delete; SQL; $sql_queries[] = <<prefix}fg_data_to_delete ( `id` bigint(20) unsigned NOT NULL, PRIMARY KEY (`id`) ) DEFAULT CHARSET=utf8; SQL; // Insert the imported posts IDs in the temporary table $sql_queries[] = <<prefix}fg_data_to_delete (`id`) SELECT post_id FROM $wpdb->postmeta WHERE meta_key LIKE '_fgs2wp_%' SQL; // Delete the imported posts and related data $sql_queries[] = <<comments c LEFT JOIN $wpdb->commentmeta cm ON cm.comment_id = c.comment_ID INNER JOIN {$wpdb->prefix}fg_data_to_delete del WHERE c.comment_post_ID = del.id; SQL; $sql_queries[] = <<term_relationships tr INNER JOIN {$wpdb->prefix}fg_data_to_delete del WHERE tr.object_id = del.id; SQL; $sql_queries[] = <<posts p LEFT JOIN $wpdb->postmeta pm ON pm.post_id = p.ID INNER JOIN {$wpdb->prefix}fg_data_to_delete del WHERE p.post_parent = del.id AND p.post_type != 'attachment'; -- Don't remove the old medias attached to posts SQL; $sql_queries[] = <<posts p LEFT JOIN $wpdb->postmeta pm ON pm.post_id = p.ID INNER JOIN {$wpdb->prefix}fg_data_to_delete del WHERE p.ID = del.id; SQL; // Truncate the temporary table $sql_queries[] = <<prefix}fg_data_to_delete; SQL; // Insert the imported terms IDs in the temporary table $sql_queries[] = <<prefix}fg_data_to_delete (`id`) SELECT term_id FROM $wpdb->termmeta WHERE meta_key LIKE '_fgs2wp_%' SQL; // Delete the imported terms and related data $sql_queries[] = <<terms t LEFT JOIN $wpdb->term_taxonomy tt ON tt.term_id = t.term_id LEFT JOIN $wpdb->termmeta tm ON tm.term_id = t.term_id INNER JOIN {$wpdb->prefix}fg_data_to_delete del WHERE t.term_id = del.id; SQL; // Truncate the temporary table $sql_queries[] = <<prefix}fg_data_to_delete; SQL; // Insert the imported comments IDs in the temporary table $sql_queries[] = <<prefix}fg_data_to_delete (`id`) SELECT comment_id FROM $wpdb->commentmeta WHERE meta_key LIKE '_fgs2wp_%' SQL; // Delete the imported comments and related data $sql_queries[] = <<comments c LEFT JOIN $wpdb->commentmeta cm ON cm.comment_id = c.comment_ID INNER JOIN {$wpdb->prefix}fg_data_to_delete del WHERE c.comment_ID = del.id; SQL; } // Execute SQL queries if ( count($sql_queries) > 0 ) { foreach ( $sql_queries as $sql ) { $result &= $wpdb->query($sql); } } // Hook for doing other actions after emptying the database do_action('fgs2wp_post_empty_database', $action); // Drop the temporary table $wpdb->query("DROP TEMPORARY TABLE IF EXISTS {$wpdb->prefix}fg_data_to_delete;"); // Reset the SPIP import counters update_option('fgs2wp_last_spip_article_id', 0); update_option('fgs2wp_last_spip_news_id', 0); update_option('fgs2wp_last_category_id', 0); // Re-count categories and tags items $this->terms_count(); // Update cache $this->clean_cache(); delete_transient('wc_count_comments'); $this->optimize_database(); $this->progressbar->set_total_count(0); $wpdb->hide_errors(); return ($result !== false); } /** * Optimize the database * */ protected function optimize_database() { global $wpdb; $sql = <<commentmeta` , `$wpdb->comments` , `$wpdb->options` , `$wpdb->postmeta` , `$wpdb->posts` , `$wpdb->terms` , `$wpdb->term_relationships` , `$wpdb->term_taxonomy`, `$wpdb->termmeta` SQL; $wpdb->query($sql); } /** * Test the database connection * * @return boolean */ function test_database_connection() { global $spip_db; if ( $this->spip_connect() ) { // Test that the "articles" table exists if ( $this->table_exists('articles') ) { $this->display_admin_notice(__('Connected with success to the SPIP database', 'fg-spip-to-wp')); do_action('fgs2wp_post_test_database_connection'); return true; } else { $this->display_admin_error(__('Couldn\'t connect to the SPIP database. Please check your parameters. And be sure the WordPress server can access the SPIP database.', 'fg-spip-to-wp') . "
\n"); } } $spip_db = null; return false; } /** * Get some Spip information * */ public function get_spip_info() { $message = __('Spip data found:', 'fg-spip-to-wp') . "\n"; // Categories $cat_count = $this->get_categories_count(); $message .= sprintf(_n('%d section', '%d sections', $cat_count, 'fg-spip-to-wp'), $cat_count) . "\n"; // Articles $articles_count = $this->get_articles_count(); $message .= sprintf(_n('%d article', '%d articles', $articles_count, 'fg-spip-to-wp'), $articles_count) . "\n"; // News $news_count = $this->get_news_count(); $message .= sprintf(_n('%d news', '%d news', $news_count, 'fg-spip-to-wp'), $news_count) . "\n"; $message = apply_filters('fgs2wp_pre_display_spip_info', $message); $this->display_admin_notice($message); } /** * Get the number of Spip categories * * @return int Number of categories */ private function get_categories_count() { $prefix = $this->plugin_options['prefix']; $sql = " SELECT COUNT(*) AS nb FROM ${prefix}rubriques r "; $result = $this->spip_query($sql); $cat_count = isset($result[0]['nb'])? $result[0]['nb'] : 0; return $cat_count; } /** * Get the number of Spip articles * * @return int Number of articles */ private function get_articles_count() { $prefix = $this->plugin_options['prefix']; $sql = " SELECT COUNT(*) AS nb FROM ${prefix}articles a WHERE a.statut IN ('publie', 'prepa', 'prop') "; $result = $this->spip_query($sql); $articles_count = isset($result[0]['nb'])? $result[0]['nb'] : 0; return $articles_count; } /** * Get the number of Spip news * * @return int Number of news */ private function get_news_count() { $prefix = $this->plugin_options['prefix']; $sql = " SELECT COUNT(*) AS nb FROM ${prefix}breves b WHERE b.statut IN ('publie', 'prepa', 'prop') "; $result = $this->spip_query($sql); $news_count = isset($result[0]['nb'])? $result[0]['nb'] : 0; return $news_count; } /** * Save the plugin options * */ private function save_plugin_options() { $this->plugin_options = array_merge($this->plugin_options, $this->validate_form_info()); update_option('fgs2wp_options', $this->plugin_options); // Hook for doing other actions after saving the options do_action('fgs2wp_post_save_plugin_options'); } /** * Validate POST info * * @return array Form parameters */ private function validate_form_info() { // Add http:// before the URL if it is missing $url = esc_url(filter_input(INPUT_POST, 'url', FILTER_SANITIZE_URL)); if ( !empty($url) && (preg_match('#^https?://#', $url) == 0) ) { $url = 'http://' . $url; } return array( 'automatic_empty' => filter_input(INPUT_POST, 'automatic_empty', FILTER_VALIDATE_BOOLEAN), 'driver' => filter_input(INPUT_POST, 'driver', FILTER_SANITIZE_STRING), 'hostname' => filter_input(INPUT_POST, 'hostname', FILTER_SANITIZE_STRING), 'port' => filter_input(INPUT_POST, 'port', FILTER_SANITIZE_NUMBER_INT), 'database' => filter_input(INPUT_POST, 'database', FILTER_SANITIZE_STRING), 'username' => filter_input(INPUT_POST, 'username'), 'password' => filter_input(INPUT_POST, 'password'), 'sqlite_file' => filter_input(INPUT_POST, 'sqlite_file', FILTER_SANITIZE_STRING), 'prefix' => filter_input(INPUT_POST, 'prefix', FILTER_SANITIZE_STRING), 'introtext' => filter_input(INPUT_POST, 'introtext', FILTER_SANITIZE_STRING), 'archived_posts' => filter_input(INPUT_POST, 'archived_posts', FILTER_SANITIZE_STRING), 'skip_media' => filter_input(INPUT_POST, 'skip_media', FILTER_VALIDATE_BOOLEAN), 'media_import_method' => filter_input(INPUT_POST, 'media_import_method', FILTER_SANITIZE_STRING), 'url' => $url, 'root_directory' => filter_input(INPUT_POST, 'root_directory', FILTER_SANITIZE_STRING), 'logo' => filter_input(INPUT_POST, 'logo', FILTER_SANITIZE_STRING), 'import_external' => filter_input(INPUT_POST, 'import_external', FILTER_VALIDATE_BOOLEAN), 'import_duplicates' => filter_input(INPUT_POST, 'import_duplicates', FILTER_VALIDATE_BOOLEAN), 'force_media_import' => filter_input(INPUT_POST, 'force_media_import', FILTER_VALIDATE_BOOLEAN), 'import_as_pages' => filter_input(INPUT_POST, 'import_as_pages', FILTER_VALIDATE_BOOLEAN), 'timeout' => filter_input(INPUT_POST, 'timeout', FILTER_SANITIZE_NUMBER_INT), 'logger_autorefresh' => filter_input(INPUT_POST, 'logger_autorefresh', FILTER_VALIDATE_BOOLEAN), ); } /** * Import * */ private function import() { if ( $this->spip_connect() ) { $time_start = microtime(true); define('WP_IMPORTING', true); update_option('fgs2wp_stop_import', false, false); // Reset the stop import action // To solve the issue of links containing ":" in multisite mode kses_remove_filters(); // Check prerequesites before the import $do_import = apply_filters('fgs2wp_pre_import_check', true); if ( !$do_import) { return; } $total_elements_count = $this->get_total_elements_count(); $this->progressbar->set_total_count($total_elements_count); $this->post_type = ($this->plugin_options['import_as_pages'] == 1) ? 'page' : 'post'; $this->media_path = $this->get_image_path(); // Hook for doing other actions before the import do_action('fgs2wp_pre_import'); // Categories if ( !isset($this->premium_options['skip_categories']) || !$this->premium_options['skip_categories'] ) { $cat_count = $this->import_categories(); $this->display_admin_notice(sprintf(_n('%d category imported', '%d categories imported', $cat_count, 'fg-spip-to-wp'), $cat_count)); } // Set the list of previously imported categories $this->imported_categories = $this->get_term_metas_by_metakey('_fgs2wp_old_category_id'); // Articles and medias if ( !isset($this->premium_options['skip_articles']) || !$this->premium_options['skip_articles'] ) { $this->import_articles(); } // News if ( !isset($this->premium_options['skip_news']) || !$this->premium_options['skip_news'] ) { $this->import_news(); } $this->display_admin_notice(sprintf(_n('%d post imported', '%d posts imported', $this->posts_count, 'fg-spip-to-wp'), $this->posts_count)); if ( $this->pages_count > 0 ) { $this->display_admin_notice(sprintf(_n('%d page imported', '%d pages imported', $this->pages_count, 'fg-spip-to-wp'), $this->pages_count)); } $this->display_admin_notice(sprintf(_n('%d media imported', '%d medias imported', $this->media_count, 'fg-spip-to-wp'), $this->media_count)); if ( !$this->import_stopped() ) { // Hook for doing other actions after the import do_action('fgs2wp_post_import'); } // Hook for other notices do_action('fgs2wp_import_notices'); // Debug info if ( defined('WP_DEBUG') && WP_DEBUG ) { $this->display_admin_notice(sprintf("Memory used: %s bytes
\n", number_format(memory_get_usage()))); $time_end = microtime(true); $this->display_admin_notice(sprintf("Duration: %d sec
\n", $time_end - $time_start)); } $this->display_admin_notice(__("Don't forget to modify internal links.", 'fg-spip-to-wp')); $this->display_admin_notice("IMPORT COMPLETED"); wp_cache_flush(); } } /** * Actions to do before the import * * @param bool $import_doable Can we start the import? * @return bool Can we start the import? */ public function pre_import_check($import_doable) { if ( $import_doable ) { if ( !$this->plugin_options['skip_media'] ) { if ( ($this->plugin_options['media_import_method'] == 'http') && empty($this->plugin_options['url']) ) { $this->display_admin_error(__('The URL field is required to import the media.', 'fg-spip-to-wp')); $import_doable = false; } elseif ( ($this->plugin_options['media_import_method'] == 'local') && empty($this->plugin_options['root_directory']) ) { $this->display_admin_error(__('The root directory field is required to import the media.', 'fg-spip-to-wp')); $import_doable = false; } } } return $import_doable; } /** * Get the number of elements to import * * @return int Number of elements to import */ private function get_total_elements_count() { $count = 0; // Categories if ( !isset($this->premium_options['skip_categories']) || !$this->premium_options['skip_categories'] ) { $count += $this->get_categories_count(); } // Articles if ( !isset($this->premium_options['skip_articles']) || !$this->premium_options['skip_articles'] ) { $count += $this->get_articles_count(); } // News if ( !isset($this->premium_options['skip_news']) || !$this->premium_options['skip_news'] ) { $count += $this->get_news_count(); } $count = apply_filters('fgs2wp_get_total_elements_count', $count); return $count; } /** * Import categories * * @return int Number of categories imported */ private function import_categories() { $imported_categories_count = 0; $all_categories = array(); if ( $this->import_stopped() ) { return 0; } $this->log(__('Importing categories...', 'fg-spip-to-wp')); // Hook before importing the categories do_action('fgs2wp_pre_import_categories'); do { if ( $this->import_stopped() ) { break; } $categories = $this->get_categories($this->chunks_size); // Get the Spip categories $categories_count = count($categories); if ( ($categories != null) && (count($categories) > 0) ) { $all_categories = array_merge($all_categories, $categories); // Insert the categories $imported_categories_count += $this->insert_categories($categories); } } while ( ($categories != null) && ($categories_count > 0) ); $all_categories = apply_filters('fgs2wp_import_categories', $all_categories); // Update the categories with their parent ids // We need to do it in a second step because the children categories // may have been imported before their parent $this->update_parent_categories($all_categories); if ( !$this->import_stopped() ) { // Hook after importing all the categories do_action('fgs2wp_post_import_categories', $all_categories); } return $imported_categories_count; } /** * Insert a list of categories in the database * * @param array $categories List of categories * @param string $taxonomy Taxonomy * @param string $last_category_metakey Last category meta key * @return int Number of inserted categories */ public function insert_categories($categories, $taxonomy='category', $last_category_metakey='fgs2wp_last_category_id') { $cat_count = 0; $processed_cat_count = count($categories); $term_metakey = '_fgs2wp_old_category_id'; // Set the list of previously imported categories $this->imported_categories = $this->get_term_metas_by_metakey($term_metakey); $terms = array(); if ( $taxonomy == 'category') { $terms[] = '1'; // unclassified category } foreach ( $categories as $category ) { $category_id = $category['id_rubrique']; $category['texte'] = $this->convert_spip1_longblob($category['texte']); // SPIP 1.x LONGBLOB // Check if the category is already imported if ( array_key_exists($category_id, $this->imported_categories) ) { continue; // Do not import already imported category } // Store the last ID to resume the import where it left off update_option($last_category_metakey, $category_id); $parent_id = isset($this->imported_categories[$category['id_parent']])? $this->imported_categories[$category['id_parent']]: 0; $new_cat_id = $this->insert_category($category, $term_metakey, $parent_id); if ( is_wp_error($new_cat_id) ) { continue; } $cat_count++; $terms[] = $new_cat_id; $this->imported_categories[$category_id] = $new_cat_id; // Hook after importing the category do_action('fgs2wp_post_import_category', $new_cat_id, $category); } $this->progressbar->increment_current_count($processed_cat_count); // Update cache if ( !empty($terms) ) { wp_update_term_count_now($terms, $taxonomy); $this->clean_cache($terms, $taxonomy); } return $cat_count; } /** * Insert a category in the database * * @since 2.5.0 * * @param array $category Category * @param string $term_metakey Term meta_key * @param int $parent_id Category parent ID * @return int Category ID */ public function insert_category($category, $term_metakey, $parent_id=0) { $category_id = $category['id_rubrique']; $category['titre'] = $this->remove_id($category['titre']); $category['titre'] = FG_Spip_to_WordPress_Tools::fix_encoding($category['titre']); $category_slug = sanitize_title($category['titre']); $category['texte'] = FG_Spip_to_WordPress_Tools::fix_encoding($category['texte']); // Insert the category $new_category = array( 'cat_name' => $category['titre'], 'category_description' => $this->spip_format($category['texte']), 'category_nicename' => $category_slug, 'category_parent' => $parent_id, ); // Hook before inserting the category $new_category = apply_filters('fgs2wp_pre_insert_category', $new_category, $category); $new_cat_id = wp_insert_category($new_category, true); if ( !is_wp_error($new_cat_id) ) { // Store the Spip category ID add_term_meta($new_cat_id, $term_metakey, $category_id, true); // Hook after inserting the category do_action('fgs2wp_post_insert_category', $new_cat_id, $category); } else { if ( isset($new_cat_id->error_data['term_exists']) ) { // Store the Spip category ID add_term_meta($new_cat_id->error_data['term_exists'], $term_metakey, $category_id, false); } } return $new_cat_id; } /** * Update the parent categories * * @param array $categories Categories * @param string $taxonomy Taxonomy */ public function update_parent_categories($categories, $taxonomy='category') { foreach ( $categories as $category ) { // Parent category if ( isset($this->imported_categories[$category['id_rubrique']]) && !empty($category['id_parent']) && isset($this->imported_categories[$category['id_parent']]) ) { $cat_id = $this->imported_categories[$category['id_rubrique']]; $parent_cat_id = $this->imported_categories[$category['id_parent']]; wp_update_term($cat_id, $taxonomy, array('parent' => $parent_cat_id)); } } } /** * Convert SPIP format to HTML * * @param string $text SPIP text * @return string HTML text */ public function spip_format($text) { $text = str_replace('{{{', '

', $text); $text = str_replace('}}}', '

', $text); $text = str_replace('{{', '', $text); $text = str_replace('}}', '', $text); $text = str_replace('{', '', $text); $text = str_replace('}', '', $text); $text = preg_replace('#\[\->(.+?)\]#', "$1", $text); // hyperlink with no anchor text, eg: [->http://www.spip.net] $text = preg_replace('#\[([^[]+?)\->(.+?)\]#', "$1", $text); // hyperlink, eg: [SPIP->http://www.spip.net] $text = preg_replace('#\[([^[]*?)<-\]#', "", $text); // anchor, eg: [anchor<-] // Quote $text = preg_replace('#(.*)#', '
$1
', $text); $text = preg_replace('##', '
$1
', $text); // Line feeds $text = preg_replace(",\r\n?,S", "\n", $text); $text = preg_replace(",[:space:]],iS", "\n\n\\0", $text); $text = preg_replace(",[:space:]],iS", "\\0\n\n", $text); // Tables $text = preg_replace(",^\n?[|],S", "\n\n|", $text); $text = preg_replace(",\n\n+[|],S", "\n\n\n\n|", $text); $text = preg_replace(",[|](\n\n+|\n?$),S", "|\n\n\n\n", $text); $regs = array(); if (preg_match_all(',[^|](\n[|].*[|]\n)[^|],UmsS', $text, $regs, PREG_SET_ORDER)) { foreach ($regs as $t) { $text = str_replace($t[1], $this->process_table_shortcodes($t[1]), $text); } } // Ordered and unordered lists if ( (strpos($text, "\n-*") !== false) || (strpos($text, "\n-#") !== false) ) { $text = $this->process_lists_shortcodes($text); } // Row starts $text = $this->process_row_starts($text); return $text; } /** * Convert the unordered and ordered lists SPIP shortcodes * * @param string $text Text * @return string Text */ private function process_lists_shortcodes($text) { $parags = preg_split(",\n[[:space:]]*\n,S", $text); $text = ''; $regs = array(); $pile_li = array(); $pile_type = array(); foreach ( $parags as $para ) { $niveau = 0; $lignes = explode("\n-", "\n" . $para); $type = ''; $first_item = true; foreach ( $lignes as $item ) { if ( $first_item ) { $text .= $item; $first_item = false; continue; } if ( preg_match(",^([*]*|[#]*)([^*#].*)$,sS", $item, $regs) ) { $profond = strlen($regs[1]); if ( $profond > 0 ) { $ajout=''; $nouv_type = (substr($item,0,1) == '*') ? 'ul' : 'ol'; $change_type = ($type AND ($type <> $nouv_type) AND ($profond == $niveau)) ? 1 : 0; $type = $nouv_type; while ( $niveau > $profond - $change_type ) { $ajout .= $pile_li[$niveau]; $ajout .= $pile_type[$niveau]; if (!$change_type) { unset ($pile_li[$niveau]); } $niveau --; } if ( $niveau == $profond && !$change_type ) { $ajout .= $pile_li[$niveau]; } while ( $niveau < $profond ) { if ( $niveau == 0 ) { $ajout .= "\n\n"; } $niveau ++; $ajout .= "<$type>"; $pile_type[$niveau] = ""; } $ajout .= "
  • "; $pile_li[$profond] = "
  • "; } else { $ajout = "\n-"; } $text .= $ajout . $regs[2]; } } $ajout = ''; while ( $niveau > 0 ) { $ajout .= $pile_li[$niveau]; $ajout .= $pile_type[$niveau]; $niveau --; } $text .= $ajout; $text .= "\n\n"; } return substr($text, 0, -2); } /** * Convert the SPIP table shorcodes to HTML * * @since 2.14.0 * * @param string $text Text * @return string Text */ private function process_table_shortcodes($text) { $regs = array(); $thead = array(); $cols = array(); preg_match_all(',([|].*)[|]\n,UmsS', $text, $regs, PREG_PATTERN_ORDER); $lignes = array(); $debut_table = $summary = ''; $l = 0; $numeric = true; $reg_line1 = ',^(\|(' . _RACCOURCI_TH_SPAN . '))+$,sS'; $reg_line_all = ',^' . _RACCOURCI_TH_SPAN . '$,sS'; foreach ($regs[1] as $ligne) { $l++; if ($l == 1) { $cap = array(); if (preg_match(',^\|\|([^|]*)(\|(.*))?$,sS', rtrim($ligne,'|'), $cap)) { $l = 0; $caption = trim($cap[1]); if ( !empty($caption) ) { $debut_table .= "".$caption."\n"; } $summary = isset($cap[3])? ' summary="'.esc_html(trim($cap[3])).'"' : ''; } else if ( preg_match($reg_line1, $ligne, $thead) ) { preg_match_all('/\|([^|]*)/S', $ligne, $cols); $ligne = ''; $cols = $cols[1]; $colspan = 1; for ( $c = count($cols)-1; $c >= 0; $c-- ) { $attr = ''; if ( $cols[$c] == '<' ) { $colspan++; } else { if ( $colspan > 1 ) { $attr = " colspan='$colspan'"; $colspan = 1; } $ligne = "$cols[$c]$ligne"; } } $debut_table .= "" . $ligne."\n"; $l = 0; } } if ($l) { if ( strpos($ligne,"\n-*") !== false OR strpos($ligne,"\n-#") !== false ) { $ligne = $this->process_lists_shortcodes($ligne); } $ligne = preg_replace("/\n{2,}/", "
    \n", $ligne); preg_match_all('/\|([^|]*)/S', $ligne, $cols); $lignes[] = $cols[1]; } } $rowspans = $numeric = array(); $n = count($lignes[0]); $k = count($lignes); for ( $i = 0; $i < $n; $i++ ) { $align = true; for ( $j = 0; $j < $k; $j++ ) { $rowspans[$j][$i] = 1; } for ( $j = 0; $j < $k; $j++ ) { $cell = trim($lignes[$j][$i]); if ( preg_match($reg_line_all, $cell) ) { $r = array(); if ( !preg_match('/^\d+([.,]?)\d*$/', $cell, $r) ) { $align = ''; break; } elseif ($r[1]) { $align = $r[1]; } } } $numeric[$i] = !$align ? '' : (" style='text-align: " . (/* $align !== true ?"\"$align\"" : */ 'right') . "'"); } $html = ''; for ( $l = count($lignes)-1; $l >= 0; $l-- ) { $cols = $lignes[$l]; $colspan = 1; $ligne = ''; for ( $c = count($cols)-1; $c >= 0; $c-- ) { $attr = isset($numeric[$c])? $numeric[$c] : ''; $cell = trim($cols[$c]); if ( $cell == '<' ) { $colspan++; } elseif( $cell == '^' ) { $rowspans[$l-1][$c] += $rowspans[$l][$c]; } else { if( $colspan>1 ) { $attr .= " colspan='$colspan'"; $colspan = 1; } if ( isset($rowspans[$l][$c]) && ($x=$rowspans[$l][$c]) > 1 ) { $attr .= " rowspan='$x'"; } $ligne = "\n' . $cols[$c] . '' . $ligne; } } $class = $this->alternate($l+1, 'even', 'odd'); $html = "$ligne\n$html"; } return "\n\n\n" . $debut_table . "\n" . $html . "\n" . "\n\n"; } /** * Filter |alternate * * @param int $i Row number * @return mixed */ private function alternate($i) { $num = func_num_args(); $args = func_get_args(); if ( $num == 2 && is_array($args[1]) ) { $args = $args[1]; array_unshift($args,''); $num = count($args); } return $args[(intval($i)-1)%($num-1)+1]; } /** * Replace the starts of the rows * * @since 2.14.0 * * @param string $text Text * @return string Text */ private function process_row_starts($text) { $text = preg_replace( array( "/\n(----+|____+)/S", "/\n-- */S", "/\n_ +/S" ), array( "\n\n
    \n\n", "\n
    — ", "\n
    " ), $text ); return $text; } /** * Clean the cache * */ public function clean_cache($terms = array()) { delete_option("category_children"); clean_term_cache($terms, 'category'); } /** * Import articles * * @return bool Import successful or not */ private function import_articles() { $this->log(__('Importing articles...', 'fg-spip-to-wp')); // Anti-duplicate $this->test_antiduplicate = false; // Hook for doing other actions before the import do_action('fgs2wp_pre_import_articles'); do { if ( $this->import_stopped() ) { break; } $posts = $this->get_articles($this->chunks_size); // Get the Spip articles $posts_count = count($posts); if ( is_array($posts) ) { foreach ( $posts as $post ) { $new_post_id = $this->import_article($post); if ( $new_post_id === false ) { return false; } // Hook for doing other actions after importing the post do_action('fgs2wp_post_import_post', $new_post_id, $post, 'article'); } } $this->progressbar->increment_current_count($posts_count); } while ( ($posts != null) && ($posts_count > 0) ); if ( !$this->import_stopped() ) { // Hook for doing other actions after the import do_action('fgs2wp_post_import_articles'); } return true; } /** * Import an article * * @since 2.0.0 * * @param array $post Post data * @return int new post ID | false | WP_Error */ public function import_article($post) { $object_type = 'article'; $post['titre'] = FG_Spip_to_WordPress_Tools::fix_encoding($post['titre']); $post['texte'] = $this->convert_spip1_longblob($post['texte']); // SPIP 1.x LONGBLOB $post['texte'] = FG_Spip_to_WordPress_Tools::fix_encoding($post['texte']); $post['chapo'] = FG_Spip_to_WordPress_Tools::fix_encoding($post['chapo']); // Anti-duplicate if ( !$this->test_antiduplicate ) { sleep(2); $test_post_id = $this->get_wp_post_id_from_spip_article_id($post['id']); if ( !empty($test_post_id) ) { $this->display_admin_error(__('The import process is still running. Please wait before running it again.', 'fg-spip-to-wp')); return false; } $this->test_antiduplicate = true; } $post['titre'] = $this->remove_id($post['titre']); // Remove the ID from the title // Hook for modifying the Spip post before processing $post = apply_filters('fgs2wp_pre_process_post', $post, $object_type); // Date $post_date = $this->sanitize_date($post['date']); $content = $post['texte']; $chapo = $post['chapo']; // Medias if ( !$this->plugin_options['skip_media'] ) { // Featured image $featured_image = $this->get_featured_image($post['id'], $object_type); list($featured_image, $post) = apply_filters('fgs2wp_pre_import_media', array($featured_image, $post)); $img_featured_image = ''; if ( !empty($featured_image) ) { $img_featured_image = ''; } // Replace documents shortcodes $chapo = $this->replace_document_shortcodes($chapo); $content = $this->replace_document_shortcodes($content); // Import media $result = $this->import_media_from_content($img_featured_image . $chapo . $content, $post_date); $post_media = $result['media']; $this->media_count += $result['media_count']; // Add the logo into the content if ( ($this->plugin_options['logo'] != 'as_featured') && !empty($img_featured_image) ) { $content = '

    ' . $img_featured_image . '

    ' . $content; } } else { // Skip media $post_media = array(); } // Category $categories_ids = array(); $category_id = $post['id_rubrique']; if ( array_key_exists($category_id, $this->imported_categories) ) { $categories_ids[] = $this->imported_categories[$category_id]; } if ( count($categories_ids) == 0 ) { $categories_ids[] = 1; // default category } // Header $excerpt = ''; if ( !empty($chapo) ) { switch ( $this->plugin_options['introtext'] ) { case 'in_excerpt': $excerpt = $chapo; break; case 'in_content': $content = '

    ' . $chapo . '

    ' . "\n\n" . $content; break; case 'in_excerpt_and_content': $excerpt = $chapo; $content = '

    ' . $chapo . '

    ' . $content; break; } } // Process content $excerpt = $this->process_content($excerpt, $post_media); $content = $this->process_content($content, $post_media); // Insert the post $new_post = array( 'post_category' => $categories_ids, 'post_content' => $content, 'post_date' => $post_date, 'post_excerpt' => $excerpt, 'post_status' => $this->get_status($post['statut']), 'post_title' => $post['titre'], 'post_name' => sanitize_title($post['titre']), 'post_type' => $this->post_type, 'comment_status' => ($post['accepter_forum'] == 'non')? 'closed' : 'open', ); // Hook for modifying the WordPress post just before the insert $new_post = apply_filters('fgs2wp_pre_insert_post', $new_post, $post, $object_type); $new_post_id = wp_insert_post($new_post, true); // Increment the Spip last imported article ID update_option('fgs2wp_last_spip_article_id', $post['id']); if ( is_wp_error($new_post_id) ) { $this->display_admin_error(sprintf(__('Article #%d:', 'fg-spip-to-wp'), $post['id']) . ' ' . $new_post_id->get_error_message()); } else { // Add links between the post and its medias $this->add_post_media($new_post_id, $new_post, $post_media, $this->plugin_options['logo'] != 'in_content'); // Add the Spip ID as a post meta in order to modify links after add_post_meta($new_post_id, '_fgs2wp_old_article_id', $post['id'], true); if ( $this->post_type == 'page' ) { $this->pages_count++; } else { $this->posts_count++; } // Hook for doing other actions after inserting the post do_action('fgs2wp_post_insert_post', $new_post_id, $post, $object_type); } return $new_post_id; } /** * Convert a SPIP 1.x LONGBLOB to TEXT with the UTF8 encoding * * @since 2.10.0 * * @param string $text Text * @return string Text */ public function convert_spip1_longblob($text) { if ( $this->blob_encoding && ($this->spip_charset != 'utf-8') ) { $text = utf8_encode($text); } return $text; } /** * Import news * * @return bool Import successful or not */ private function import_news() { $this->log(__('Importing news...', 'fg-spip-to-wp')); // Anti-duplicate $this->test_antiduplicate = false; // Hook for doing other actions before the import do_action('fgs2wp_pre_import_news'); do { if ( $this->import_stopped() ) { break; } $posts = $this->get_news($this->chunks_size); // Get the Spip news $posts_count = count($posts); if ( is_array($posts) ) { foreach ( $posts as $post ) { $new_post_id = $this->import_one_news($post); if ( $new_post_id === false ) { return false; } // Hook for doing other actions after importing the post do_action('fgs2wp_post_import_post', $new_post_id, $post, 'breve'); } } $this->progressbar->increment_current_count($posts_count); } while ( ($posts != null) && ($posts_count > 0) ); if ( !$this->import_stopped() ) { // Hook for doing other actions after the import do_action('fgs2wp_post_import_news'); } return true; } /** * Import a news (breve) * * @since 2.0.0 * * @param array $post Post data * @return int new post ID | false | WP_Error */ public function import_one_news($post) { $object_type = 'breve'; $post['titre'] = FG_Spip_to_WordPress_Tools::fix_encoding($post['titre']); $post['texte'] = $this->convert_spip1_longblob($post['texte']); // SPIP 1.x LONGBLOB $post['texte'] = FG_Spip_to_WordPress_Tools::fix_encoding($post['texte']); // Anti-duplicate if ( !$this->test_antiduplicate ) { sleep(2); $test_post_id = $this->get_wp_post_id_from_spip_news_id($post['id']); if ( !empty($test_post_id) ) { $this->display_admin_error(__('The import process is still running. Please wait before running it again.', 'fg-spip-to-wp')); return false; } $this->test_antiduplicate = true; } // SPIP 2.0 fixes if ( version_compare($this->spip_version, '3', '<') ) { $post['titre'] = $this->remove_id($post['titre']); // Remove the ID from the title } // Hook for modifying the Spip post before processing $post = apply_filters('fgs2wp_pre_process_post', $post, $object_type); // Date $post_date = $this->sanitize_date($post['date_heure']); $content = $post['texte']; // Medias if ( !$this->plugin_options['skip_media'] ) { // Featured image $featured_image = $this->get_featured_image($post['id'], $object_type); list($featured_image, $post) = apply_filters('fgs2wp_pre_import_media', array($featured_image, $post)); $img_featured_image = ''; if ( !empty($featured_image) ) { $img_featured_image = ''; } // Replace documents shortcodes $content = $this->replace_document_shortcodes($content); // Import media $result = $this->import_media_from_content($img_featured_image . $content, $post_date); $post_media = $result['media']; $this->media_count += $result['media_count']; // Add the logo into the content if ( ($this->plugin_options['logo'] != 'as_featured') ) { $content = '

    ' . $img_featured_image . '

    ' . $content; } } else { // Skip media $post_media = array(); } // Category $categories_ids = $this->get_wp_category_ids($post['id_rubrique'], $this->imported_categories); // Process content $content = $this->process_content($content, $post_media); // Insert the post $new_post = array( 'post_category' => $categories_ids, 'post_content' => $content, 'post_excerpt' => '', 'post_date' => $post_date, 'post_status' => $this->get_status($post['statut']), 'post_title' => $post['titre'], 'post_name' => sanitize_title($post['titre']), 'post_type' => 'post', ); // Hook for modifying the WordPress post just before the insert $new_post = apply_filters('fgs2wp_pre_insert_post', $new_post, $post, $object_type); $new_post_id = wp_insert_post($new_post, true); // Increment the Spip last imported post ID update_option('fgs2wp_last_spip_news_id', $post['id']); if ( is_wp_error($new_post_id) ) { $this->display_admin_error(sprintf(__('Article #%d:', 'fg-spip-to-wp'), $post['id']) . ' ' . $new_post_id->get_error_message()); } else { // Add links between the post and its medias $this->add_post_media($new_post_id, $new_post, $post_media, $this->plugin_options['logo'] != 'in_content'); // Add the Spip ID as a post meta in order to modify links after add_post_meta($new_post_id, '_fgs2wp_old_news_id', $post['id'], true); // Add the extra SPIP fields if ( !empty($post['lien_titre']) ) { add_post_meta($new_post_id, 'lien_titre', $post['lien_titre'], true); } if ( !empty($post['lien_url']) ) { add_post_meta($new_post_id, 'lien_url', $post['lien_url'], true); } $this->posts_count++; // Hook for doing other actions after inserting the post do_action('fgs2wp_post_insert_post', $new_post_id, $post, $object_type); } return $new_post_id; } /** * Get the WordPress status from the SPIP status * * @since 1.9.0 * * @param string $spip_status SPIP status * @return string WordPress status */ public function get_status($spip_status) { switch ( $spip_status ) { case 'publie': // published $status = 'publish'; break; case 'prop': // pending $status = 'pending'; break; default: $status = 'draft'; } return $status; } /** * Remove the ID at the beginning of a string * * @param string $string String * @return string String */ public function remove_id($string) { $result = preg_replace('/^\d+\. /', '', $string); return $result; } /** * Replace the articles shortcodes (art, br) in the post content * * @since 2.30.0 * * @param string $content Content * @return string Content */ private function replace_articles_shortcodes($content) { $matches = array(); // Replace the shortcodes like [anchor_text->art99] or [anchor_text->br99] if ( preg_match_all('#\[(.*?)->(art|br)(\d+)\]#', $content, $matches, PREG_SET_ORDER) > 0 ) { foreach ($matches as $match ) { $anchor_text = $match[1]; $short_object_type = $match[2]; // art | br $id = $match[3]; $object_type = ($short_object_type == 'br')? 'breve' : 'article'; if ( empty($anchor_text) ) { $anchor_text = $this->get_article_or_news_title($id, $object_type); } $replacement = '' . $anchor_text . ''; $content = preg_replace('#' . preg_quote($match[0]) . '#', $replacement, $content); } } return $content; } /** * Get the title of the article (or the news) * * @since 2.30.0 * * @param int $id Object ID * @param string $object_type Object type * @return string Title */ private function get_article_or_news_title($id, $object_type) { if ( $object_type == 'br' ) { $title = $this->get_news_title($id); } else { $title = $this->get_article_title($id); } return $title; } /** * Get a news title * * @since 2.30.0 * * @param int $id News ID * @return string Title */ private function get_news_title($id) { $title = 'Breve ' . $id; $prefix = $this->plugin_options['prefix']; $sql = " SELECT titre FROM ${prefix}breves b WHERE b.id_breve = '$id' LIMIT 1 "; $result = $this->spip_query($sql); if ( count($result) > 0 ) { $title = $result[0]['titre']; } return $title; } /** * Get an article title * * @since 2.30.0 * * @param int $id Article ID * @return string Title */ private function get_article_title($id) { $title = 'Article ' . $id; $prefix = $this->plugin_options['prefix']; $sql = " SELECT titre FROM ${prefix}articles a WHERE a.id_article = '$id' LIMIT 1 "; $result = $this->spip_query($sql); if ( count($result) > 0 ) { $title = $result[0]['titre']; } return $title; } /** * Replace the documents shortcodes (doc, img, emb, pdf) in the post content * @param string $content Content * @return string Content */ private function replace_document_shortcodes($content) { $matches = array(); $matches_align = array(); // Import the medias that use the SPIP shortcodes , , or if ( preg_match_all('#<(doc|img|emb|pdf)(\d+)(\|(.*?))?>#', $content, $matches, PREG_SET_ORDER) > 0 ) { foreach ($matches as $match ) { // $doc_embed_type = $match[1]; // doc | img | emb | pdf $doc_id = $match[2]; $doc_align = isset($match[4])? $match[4]: ''; $document = $this->get_document($doc_id); if ( !empty($document) ) { $filename = $document['fichier']; if ( !preg_match('/^http/', $document['fichier']) ) { $filename = $this->media_path . $filename; } if ( preg_match('/^txt=(.*)/', $doc_align, $matches_align) ) { $doc_title = $matches_align[1]; } elseif ( !empty($document['titre']) ) { $doc_title = $document['titre']; } else { $doc_title = basename($document['fichier']); } $filetype = wp_check_filetype($filename); // Description $description = !empty($document['descriptif'])? ' data-description="' . $document['descriptif'] . '"' : ''; if ( preg_match('/image/', $filetype['type']) ) { // Image // Alignment $alignment = ''; if ( !empty($doc_align) ) { $alignment = ' align="' . $doc_align . '"'; } if ( !empty($document['titre']) ) { // With caption $replacement = '' . $doc_title . ''; } else { $replacement = '' . $doc_title . ''; } } elseif ( preg_match('#^https://www.youtube.com#', $filename) ) { // Embed YouTube video $replacement = '[embed]' . $filename . '[/embed]'; } else { // Not an image $replacement = '' . $doc_title . ''; } $content = preg_replace('#' . preg_quote($match[0]) . '#', $replacement, $content); } } } // Import the medias that use the SPIP shortcodes [anchor_text->img] if ( preg_match_all('#\[(.*?)->(doc|img|emb|pdf)(\d+)\]#', $content, $matches, PREG_SET_ORDER) > 0 ) { foreach ($matches as $match ) { $anchor_text = $match[1]; // $doc_embed_type = $match[2]; // doc | img | emb | pdf $doc_id = $match[3]; $document = $this->get_document($doc_id); if ( !empty($document) ) { $filename = $this->media_path . $document['fichier']; // Description $description = !empty($document['descriptif'])? ' data-description="' . $document['descriptif'] . '"' : ''; $replacement = '' . $anchor_text . ''; $content = preg_replace('#' . preg_quote($match[0]) . '#', $replacement, $content); } } } return $content; } /** * Get the matching WP category ID from a SPIP category * * @since 2.5.0 * * @param int $category_id SPIP category ID * @param array $imported_categories Mapping table of SPIP and WP categories IDs * @return array List of categories IDs */ public function get_wp_category_ids($category_id, $imported_categories) { $categories_ids = array(); if ( array_key_exists($category_id, $imported_categories) ) { $categories_ids[] = $imported_categories[$category_id]; } if ( count($categories_ids) == 0 ) { $categories_ids[] = 1; // default category } return $categories_ids; } /** * Stop the import * */ public function stop_import() { update_option('fgs2wp_stop_import', true); } /** * Test if the import needs to stop * * @return boolean Import needs to stop or not */ public function import_stopped() { return get_option('fgs2wp_stop_import'); } /** * Get Spip categories * * @param int $limit Number of categories max * @return array of Categories */ protected function get_categories($limit=1000) { $categories = array(); $prefix = $this->plugin_options['prefix']; $last_category_id = (int)get_option('fgs2wp_last_category_id'); // to restore the import where it left $sql = " SELECT r.id_rubrique, r.id_parent, r.titre, r.descriptif, r.texte FROM ${prefix}rubriques r WHERE r.id_rubrique > '$last_category_id' ORDER BY r.id_rubrique LIMIT $limit "; $sql = apply_filters('fgs2wp_get_categories_sql', $sql, $prefix); $categories = $this->spip_query($sql); $categories = apply_filters('fgs2wp_get_categories', $categories); return $categories; } /** * Get Spip articles * * @param int $limit Number of articles max * @return array of articles */ protected function get_articles($limit=1000) { $articles = array(); $last_spip_article_id = (int)get_option('fgs2wp_last_spip_article_id'); // to restore the import where it left $prefix = $this->plugin_options['prefix']; // Hooks for adding extra cols and extra joins $extra_cols = apply_filters('fgs2wp_get_posts_add_extra_cols', '', 'article'); $extra_joins = apply_filters('fgs2wp_get_posts_add_extra_joins', '', 'article'); $sql = " SELECT a.id_article AS id, a.titre, a.id_rubrique, a.chapo, a.texte, a.date, a.statut, a.visites, a.accepter_forum $extra_cols FROM ${prefix}articles a $extra_joins WHERE a.statut NOT IN ('refuse', 'poubelle') -- don't get the cancelled and the removed articles AND a.id_article > '$last_spip_article_id' ORDER BY a.id_article LIMIT $limit "; $sql = apply_filters('fgs2wp_get_posts_sql', $sql, $prefix, $extra_cols, $extra_joins, $last_spip_article_id, $limit); $articles = $this->spip_query($sql); return $articles; } /** * Get Spip news * * @param int $limit Number of news max * @return array of news */ protected function get_news($limit=1000) { $news = array(); $last_spip_news_id = (int)get_option('fgs2wp_last_spip_news_id'); // to restore the import where it left $prefix = $this->plugin_options['prefix']; // Hooks for adding extra cols and extra joins $extra_cols = apply_filters('fgs2wp_get_posts_add_extra_cols', '', 'breve'); $extra_joins = apply_filters('fgs2wp_get_posts_add_extra_joins', '', 'breve'); $sql = " SELECT b.id_breve AS id, b.titre, b.id_rubrique, b.texte, b.date_heure, b.statut, b.lien_titre, b.lien_url $extra_cols FROM ${prefix}breves b $extra_joins WHERE b.statut NOT IN ('refuse', 'poubelle') -- don't get the cancelled and the removed news AND b.id_breve > '$last_spip_news_id' ORDER BY b.id_breve LIMIT $limit "; $sql = apply_filters('fgs2wp_get_posts_sql', $sql, $prefix, $extra_cols, $extra_joins, $last_spip_news_id, $limit); $news = $this->spip_query($sql); return $news; } /** * Get the featured image of an article or from a news * * @param int $post_id * @param string $object_type article | breve * @return string Image path */ protected function get_featured_image($post_id, $object_type) { $images_path = trailingslashit($this->get_media_root_path()) . $this->media_path; // Object type switch ( $object_type ) { case 'article': $prefix = 'arton'; break; case 'breve': $prefix = 'breveon'; break; default: return ''; // unknown type } // Extensions $extensions = array('jpg', 'JPG', 'png', 'PNG', 'gif', 'GIF'); foreach ( $extensions as $extension ) { $image_file_path = $images_path . $prefix . $post_id . '.' . $extension; if ( $this->file_exists($image_file_path) ) { return $image_file_path; } } return ''; } /** * Get the media root path * * @since 2.3.0 * * @return string Media root path */ public function get_media_root_path() { $path = ''; switch ( $this->plugin_options['media_import_method'] ) { case 'http': $path = $this->plugin_options['url']; break; case 'local': $path = $this->plugin_options['root_directory']; break; } return $path; } /** * Test if a file exists * * @since 2.3.0 * * @param string $filePath * @return boolean True if the file exists */ public function file_exists($filePath) { switch ( $this->plugin_options['media_import_method'] ) { case 'http': return $this->url_exists($filePath); case 'local': return file_exists($filePath); } } /** * Test if a remote file exists * * @param string $filePath * @return boolean True if the file exists */ public function url_exists($filePath) { $url = str_replace(' ', '%20', $filePath); // Try the get_headers method $headers = @get_headers($url); $result = preg_match("/200/", $headers[0]); if ( !$result && strpos($filePath, 'https:') !== 0 ) { // Try the fsock method $url = str_replace('http://', '', $url); if ( strstr($url, '/') ) { $url = explode('/', $url, 2); $url[1] = '/' . $url[1]; } else { $url = array($url, '/'); } $fh = fsockopen($url[0], 80); if ( $fh ) { fputs($fh,'GET ' . $url[1] . " HTTP/1.1\nHost:" . $url[0] . "\n"); fputs($fh,"User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36\n\n"); $response = fread($fh, 22); fclose($fh); $result = (strpos($response, '200') !== false); } else { $result = false; } } return $result; } /** * Get the image path * * @return string Image path */ public function get_image_path() { $dir_img = $this->get_spip_meta('dir_img'); if ( empty($dir_img) ) { $dir_img = 'IMG/'; } return $dir_img; } /** * Get the SPIP version * * @return string SPIP version */ private function get_spip_version() { $version = 0; $raw_plugin_info = $this->get_spip_meta('plugin'); if ( strpos($raw_plugin_info, 'a:') !== false ) { $plugin_info = @unserialize($raw_plugin_info); if ( !$plugin_info ) { $plugin_info = @unserialize(utf8_decode($raw_plugin_info)); } $version = isset($plugin_info['SPIP']['version'])? $plugin_info['SPIP']['version']: 0; } else { $version_installee = $this->get_spip_meta('version_installee'); $version = !empty($version_installee)? $version_installee: 0; } return $version; } /** * Get the meta value from the SPIP meta table * * @param string $meta_key * @return string meta value */ public function get_spip_meta($meta_key) { $meta_value = ''; $prefix = $this->plugin_options['prefix']; $sql = " SELECT m.valeur FROM ${prefix}meta m WHERE m.nom = '$meta_key' LIMIT 1 "; $result = $this->spip_query($sql); if ( isset($result[0]['valeur']) ) { $meta_value = $result[0]['valeur']; } return $meta_value; } /** * Test if the texts are encoded as blob * * @return bool */ private function is_text_encoded_as_blob() { global $spip_db; $blob_encoded = false; $prefix = $this->plugin_options['prefix']; $table = 'articles'; $column = 'texte'; switch ( $this->plugin_options['driver'] ) { case 'mysql': $sql = "SHOW COLUMNS FROM ${prefix}${table} LIKE '$column'"; break; case 'sqlite': $sql = "PRAGMA table_info(${prefix}${table})"; break; } $query = $spip_db->query($sql, PDO::FETCH_ASSOC); if ( $query !== false ) { if ( $this->plugin_options['driver'] == 'sqlite' ) { // SQLite $result = $query->fetchAll(); foreach ( $result as $row ) { if ( isset($row['name']) && ($row['name'] == $column) ) { if ( preg_match('/blob/', $row['type']) ) { $blob_encoded = true; } break; } } } else { // MySQL $result = $query->fetch(); if ( isset($result['Type']) && preg_match('/blob/', $result['Type']) ) { $blob_encoded = true; } } } return $blob_encoded; } /** * Import post medias from content * * @param string $content post content * @param date $post_date Post date (for storing media) * @param array $options Options * @return array: * array media: Medias imported * int media_count: Medias count */ public function import_media_from_content($content, $post_date, $options=array()) { $media = array(); $media_count = 0; $matches = array(); $alt_matches = array(); $title_matches = array(); $description_matches = array(); if ( preg_match_all('#<(img|a)(.*?)(src|href)="(.*?)"(.*?)>#', $content, $matches, PREG_SET_ORDER) > 0 ) { if ( is_array($matches) ) { foreach ($matches as $match ) { $filename = $match[4]; $other_attributes = $match[2] . $match[5]; // Image Alt $image_alt = ''; if (preg_match('#alt="(.*?)"#', $other_attributes, $alt_matches) ) { $image_alt = wp_strip_all_tags(stripslashes($alt_matches[1]), true); } // Image caption $image_caption = ''; if (preg_match('#title="(.*?)"#', $other_attributes, $title_matches) ) { $image_caption = $title_matches[1]; } // Image description $image_description = ''; if (preg_match('#data-description="(.*?)"#', $other_attributes, $description_matches) ) { $image_description = $description_matches[1]; } $attachment_id = $this->import_media($image_alt, $filename, $post_date, $options, $image_caption, $image_description); if ( $attachment_id !== false ) { $media_count++; $media[$filename] = $attachment_id; } } } } return array( 'media' => $media, 'media_count' => $media_count ); } /** * Import a media * * @param string $name Image name * @param string $filename Image URL * @param date $date Date * @param array $options Options * @param string $image_caption Image caption * @param string $image_description Image description * @return int attachment ID or false */ public function import_media($name, $filename, $date, $options=array(), $image_caption='', $image_description='') { if ( $date == '0000-00-00 00:00:00' ) { $date = date('Y-m-d H:i:s'); } $import_external = ($this->plugin_options['import_external'] == 1) || (isset($options['force_external']) && $options['force_external'] ); $filename = urldecode(html_entity_decode($filename)); // for filenames with spaces or accents $filetype = wp_check_filetype($filename); if ( empty($filetype['type']) || ($filetype['type'] == 'text/html') ) { // Unrecognized file type return false; } $media_root_path = $this->get_media_root_path(); // Upload the file from the Spip web site to WordPress upload dir if ( preg_match('/^http/', $filename) ) { if ( $import_external || // External file preg_match('#^' . $media_root_path . '#', $filename) // Local file ) { $old_filename = $filename; } else { return false; } } elseif ( strpos($filename, $media_root_path) !== 0 ) { // Don't add the media_root_path if it has already been added if ( strpos($filename, '/') === 0 ) { // Avoid a double slash $old_filename = untrailingslashit($media_root_path) . $filename; } else { $old_filename = trailingslashit($media_root_path) . $filename; } } else { $old_filename = $filename; } if ( $this->plugin_options['media_import_method'] == 'http' ) { $old_filename = str_replace(" ", "%20", $old_filename); // for filenames with spaces } $img_dir = strftime('%Y/%m', strtotime($date)); $uploads = wp_upload_dir($img_dir); $new_upload_dir = $uploads['path']; $new_filename = $filename; if ( $this->plugin_options['import_duplicates'] == 1 ) { // Images with duplicate names $new_filename = preg_replace('#.*'. untrailingslashit($this->media_path) . '/#', '', $new_filename); $new_filename = str_replace('http://', '', $new_filename); $new_filename = str_replace('/', '_', $new_filename); } $basename = basename($new_filename); $basename = sanitize_file_name($basename); $new_full_filename = $new_upload_dir . '/' . $basename; if ( $this->plugin_options['force_media_import'] || !file_exists($new_full_filename) || (filesize($new_full_filename) == 0) ) { // print "Copy \"$old_filename\" => $new_full_filename
    "; switch ( $this->plugin_options['media_import_method'] ) { case 'http': // HTTP remote copy if ( ! @$this->remote_copy($old_filename, $new_full_filename) ) { $error = error_get_last(); $error_message = $error['message']; $this->display_admin_error("Can't copy $old_filename to $new_full_filename : $error_message"); return false; } break; case 'local': // Local copy if ( !copy($old_filename, $new_full_filename) ) { $error = error_get_last(); $error_message = $error['message']; $this->display_admin_error("Can't copy $old_filename to $new_full_filename : $error_message"); return false; } break; } } $post_title = !empty($name)? $name : preg_replace('/\.[^.]+$/', '', $basename); // Image Alt $image_alt = ''; if ( !empty($name) ) { $image_alt = wp_strip_all_tags(stripslashes($name), true); } // GUID $upload_dir = wp_upload_dir(); $guid = str_replace($upload_dir['basedir'], $upload_dir['baseurl'], $new_full_filename); $attachment_id = $this->insert_attachment($post_title, $basename, $new_full_filename, $guid, $date, $filetype['type'], $image_alt, $image_caption, $image_description); return $attachment_id; } /** * Check if the attachment exists in the database * * @param string $name * @return object Post */ private function get_attachment_from_name($name) { $name = preg_replace('/\.[^.]+$/', '', basename($name)); $r = array( 'name' => $name, 'post_type' => 'attachment', 'numberposts' => 1, ); $posts_array = get_posts($r); if ( is_array($posts_array) && (count($posts_array) > 0) ) { return $posts_array[0]; } else { return false; } } /** * Save the attachment and generates its metadata * * @param string $attachment_title Attachment name * @param string $basename Original attachment filename * @param string $new_full_filename New attachment filename with path * @param string $guid GUID * @param date $date Date * @param string $filetype File type * @param string $image_alt Image description * @param string $image_caption Image caption * @param string $image_description Image description * @return int|false Attachment ID or false */ public function insert_attachment($attachment_title, $basename, $new_full_filename, $guid, $date, $filetype, $image_alt='', $image_caption='', $image_description='') { $post_name = 'attachment-' . sanitize_title($attachment_title); // Prefix the post name to avoid wrong redirect to a post with the same name // If the attachment does not exist yet, insert it in the database $attachment_id = 0; $attachment = $this->get_attachment_from_name($post_name); if ( $attachment ) { $attached_file = basename(get_attached_file($attachment->ID)); if ( $attached_file == $basename ) { // Check if the filename is the same (in case of the legend is not unique) $attachment_id = $attachment->ID; } } if ( $attachment_id == 0 ) { $attachment_data = array( 'guid' => $guid, 'post_date' => $date, 'post_mime_type' => $filetype, 'post_name' => $post_name, 'post_title' => $attachment_title, 'post_status' => 'inherit', 'post_content' => $image_description, 'post_excerpt' => $image_caption, ); $attachment_id = wp_insert_attachment($attachment_data, $new_full_filename); add_post_meta($attachment_id, '_fgs2wp_imported', 1, true); // To delete the imported attachments } if ( !empty($attachment_id) ) { if ( preg_match('/(image|audio|video)/', $filetype) ) { // Image, audio or video // you must first include the image.php file // for the function wp_generate_attachment_metadata() to work require_once(ABSPATH . 'wp-admin/includes/image.php'); $attach_data = wp_generate_attachment_metadata( $attachment_id, $new_full_filename ); wp_update_attachment_metadata($attachment_id, $attach_data); // Image Alt if ( !empty($image_alt) ) { update_post_meta($attachment_id, '_wp_attachment_image_alt', addslashes($image_alt)); // update_post_meta expects slashed } } return $attachment_id; } else { return false; } } /** * Get a document by its ID * * @param int $doc_id SPIP document ID * @return array document data */ private function get_document($doc_id) { $document = array(); $prefix = $this->plugin_options['prefix']; $sql = " SELECT d.id_document, d.fichier, d.titre, d.descriptif, d.taille, d.date FROM ${prefix}documents d WHERE d.id_document = '$doc_id' LIMIT 1 "; $documents = $this->spip_query($sql); if ( isset($documents[0]) ) { $document = $documents[0]; } return $document; } /** * Process the post content * * @param string $content Post content * @param array $post_media Post medias * @return string Processed post content */ public function process_content($content, $post_media=array()) { if ( !empty($content) ) { $content = $this->replace_articles_shortcodes($content); $content = $this->spip_format($content); $content = preg_replace("/\\\\+'/", "'", $content); // Fix the multiple escapes of ' // Replace media URLs with the new URLs $content = $this->process_content_media_links($content, $post_media); // Replace video links $content = $this->process_video_links($content); // For importing backslashes $content = addslashes($content); $content = apply_filters('fgs2wp_process_content', $content); } return $content; } /** * Replace media URLs with the new URLs * * @param string $content Post content * @param array $post_media Post medias * @return string Processed post content */ private function process_content_media_links($content, $post_media) { $matches = array(); $matches_caption = array(); if ( is_array($post_media) ) { // Get the attachments attributes $attachments_found = false; $medias = array(); foreach ( $post_media as $old_filename => $attachment_id ) { $media = array(); $media['attachment_id'] = $attachment_id; $media['url_old_filename'] = urlencode($old_filename); // for filenames with spaces if ( preg_match('/image/', get_post_mime_type($attachment_id)) ) { // Image $image_src = wp_get_attachment_image_src($attachment_id, 'full'); $media['new_url'] = $image_src[0]; $media['width'] = $image_src[1]; $media['height'] = $image_src[2]; } else { // Other media $media['new_url'] = wp_get_attachment_url($attachment_id); } $medias[$old_filename] = $media; $attachments_found = true; } if ( $attachments_found ) { // Remove the links from the content $this->post_link_count = 0; $this->post_link = array(); $content = preg_replace_callback('#<(a) (.*?)(href)=(.*?)#i', array($this, 'remove_links'), $content); $content = preg_replace_callback('#<(img) (.*?)(src)=(.*?)>#i', array($this, 'remove_links'), $content); // Process the stored medias links foreach ($this->post_link as &$link) { $new_link = $link['old_link']; $alignment = ''; if ( preg_match('/(align="|float: )(left|right|center)/', $new_link, $matches) ) { $alignment = 'align' . $matches[2]; } if ( preg_match_all('#(src|href)="(.*?)"#i', $new_link, $matches, PREG_SET_ORDER) ) { $caption = ''; foreach ( $matches as $match ) { $old_filename = $match[2]; $link_type = ($match[1] == 'src')? 'img': 'a'; if ( array_key_exists($old_filename, $medias) ) { $media = $medias[$old_filename]; if ( array_key_exists('new_url', $media) ) { if ( (strpos($new_link, $old_filename) > 0) || (strpos($new_link, $media['url_old_filename']) > 0) ) { // URL encode the filename $new_filename = basename($media['new_url']); $encoded_new_filename = rawurlencode($new_filename); $new_url = str_replace($new_filename, $encoded_new_filename, $media['new_url']); $new_link = preg_replace('#(' . preg_quote($old_filename) . '|' . preg_quote($media['url_old_filename']) . ')#', $new_url, $new_link, 1); if ( $link_type == 'img' ) { // images only // Define the width and the height of the image if it isn't defined yet if ((strpos($new_link, 'width=') === false) && (strpos($new_link, 'height=') === false)) { $width_assertion = isset($media['width']) && ($media['width'] != 0)? ' width="' . $media['width'] . '"' : ''; $height_assertion = isset($media['height']) && ($media['height'] != 0)? ' height="' . $media['height'] . '"' : ''; } else { $width_assertion = ''; $height_assertion = ''; } // Caption shortcode if ( preg_match('/class=".*caption.*?"/', $link['old_link']) ) { if ( preg_match('/title="(.*?)"/', $link['old_link'], $matches_caption) ) { $caption_value = str_replace('%', '%%', $matches_caption[1]); $align_value = ($alignment != '')? $alignment : 'alignnone'; $caption = '[caption id="attachment_' . $media['attachment_id'] . '" align="' . $align_value . '"' . $width_assertion . ']%s' . $caption_value . '[/caption]'; } } $align_class = ($alignment != '')? $alignment . ' ' : ''; $new_link = preg_replace('##', "', $new_link); } } } } } // Add the caption if ( $caption != '' ) { $new_link = sprintf($caption, $new_link); } } $link['new_link'] = $new_link; } // Reinsert the converted medias links $content = preg_replace_callback('#__fg_link_(\d+)__#', array($this, 'restore_links'), $content); } } return $content; } /** * Remove all the links from the content and replace them with a specific tag * * @param array $matches Result of the preg_match * @return string Replacement */ private function remove_links($matches) { $this->post_link[] = array('old_link' => $matches[0]); return '__fg_link_' . $this->post_link_count++ . '__'; } /** * Restore the links in the content and replace them with the new calculated link * * @param array $matches Result of the preg_match * @return string Replacement */ private function restore_links($matches) { $link = $this->post_link[$matches[1]]; $new_link = array_key_exists('new_link', $link)? $link['new_link'] : $link['old_link']; return $new_link; } /** * Add a link between a media and a post (parent id + thumbnail) * * @param int $post_id Post ID * @param array $post_data Post data * @param array $post_media Post medias IDs * @param boolean $set_featured_image Set the featured image? */ public function add_post_media($post_id, $post_data, $post_media, $set_featured_image=true) { $thumbnail_is_set = false; if ( is_array($post_media) ) { foreach ( $post_media as $attachment_id ) { $attachment = get_post($attachment_id); if ( !empty($attachment) ) { $attachment->post_parent = $post_id; // Attach the post to the media $attachment->post_date = $post_data['post_date'] ;// Define the media's date wp_update_post($attachment); // Set the featured image. If not defined, it is the first image of the content. if ( $set_featured_image && !$thumbnail_is_set ) { set_post_thumbnail($post_id, $attachment_id); $thumbnail_is_set = true; } } } } } /** * Modify the video links * * @param string $content Content * @return string Content */ private function process_video_links($content) { if ( strpos($content, '{"video"') !== false ) { $content = preg_replace('/(

    )?{"video":"(.*?)".*?}(<\/p>)?/', "$2", $content); } return $content; } /** * Modify the internal links of all posts * * @return array: * int links_count: Links count */ private function modify_links() { $links_count = 0; $step = 1000; // to limit the results $offset = 0; $matches = array(); // Set the list of previously imported categories $this->imported_categories = $this->get_term_metas_by_metakey('_fgs2wp_old_category_id'); // Hook for doing other actions before modifying the links do_action('fgs2wp_pre_modify_links'); do { $args = array( 'numberposts' => $step, 'offset' => $offset, 'orderby' => 'ID', 'order' => 'ASC', 'post_type' => 'any', 'post_status' => 'any', ); $posts = get_posts($args); foreach ( $posts as $post ) { $post = apply_filters('fgs2wp_post_get_post', $post); // Used to translate the links $content = $post->post_content; if ( preg_match_all('##', $content, $matches, PREG_SET_ORDER) > 0 ) { if ( is_array($matches) ) { foreach ( $matches as $match ) { $link = $match[2]; list($link_without_anchor, $anchor_link) = $this->split_anchor_link($link); // Split the anchor link // Is it an internal link ? if ( !empty($link_without_anchor) && $this->is_internal_link($link_without_anchor) ) { $new_link = ''; if ( preg_match('/^rub(\d+)$/', $link_without_anchor, $matches) ) { // Is it a link to a category? $new_link = $this->get_category_link($matches[1], $post); } else { list($article_id, $object_type) = $this->find_article_id_in_link($link_without_anchor); // Is it a link to an article or to a news? if ( $article_id != 0 ) { $new_link = $this->get_article_link($article_id, $object_type, $post); } } if ( !empty($new_link) ) { if ( !empty($anchor_link) ) { $new_link .= '#' . $anchor_link; } $content = str_replace("href=\"$link\"", "href=\"$new_link\"", $content); // Update the post wp_update_post(array( 'ID' => $post->ID, 'post_content' => $content, )); $links_count++; } } } } } } $offset += $step; } while ( ($posts != null) && (count($posts) > 0) ); // Hook for doing other actions after modifying the links do_action('fgs2wp_post_modify_links'); return array('links_count' => $links_count); } /** * Test if the link is an internal link or not * * @since 2.36.3 * * @param string $link * @return bool */ private function is_internal_link($link) { $result = (preg_match("#^".$this->plugin_options['url']."#", $link) > 0) || (preg_match("#^(http|//)#", $link) == 0); return $result; } /** * Find an article ID in a link * * @param string $link Link * @return array [Article ID, Object type] */ private function find_article_id_in_link($link) { $article_id = 0; $object_type = 'article'; $matches = array(); if ( is_numeric($link) ) { $article_id = $link; } elseif ( preg_match('/(article|breve)(\d+)/', $link, $matches)) { // article12.html or spip.php?article12 or breve12.html or spip.php?breve12 $object_type = $matches[1]; $article_id = $matches[2]; } elseif ( preg_match('/id_(article|breve)=(\d+)/', $link, $matches)) { // spip.php?page=article&id_article=12 or spip.php?page=breve&id_breve=12 $object_type = $matches[1]; $article_id = $matches[2]; } return array($article_id, $object_type); } /** * Get the WordPress article link matching the SPIP article ID * * @since 2.18.0 * * @param int $article_id SPIP article ID * @param string $object_type article | breve * @param array $post Post * @return string New link */ private function get_article_link($article_id, $object_type, $post) { $new_link = ''; if ( $object_type == 'breve' ) { // News $wp_post_id = $this->get_wp_post_id_from_spip_news_id($article_id); } else { // Article $wp_post_id = $this->get_wp_post_id_from_spip_article_id($article_id); } if ( !empty($wp_post_id) ) { $wp_post_id = apply_filters('fgs2wp_post_get_post_by_spip_id', $wp_post_id, $post); // Used to get the ID of the translated post $new_link = get_permalink($wp_post_id); } return $new_link; } /** * Get the WordPress category link matching the SPIP category ID * * @since 2.18.0 * * @param int $category_id SPIP Category ID * @return string New link */ private function get_category_link($category_id) { $new_link = ''; $wp_category_id = $this->get_wp_category_ids($category_id, $this->imported_categories); if ( count($wp_category_id) > 0 ) { $new_link = get_category_link($wp_category_id[0]); } return $new_link; } /** * Split a link by its anchor link * * @param string $link Original link * @return array(string link, string anchor_link) [link without anchor, anchor_link] */ private function split_anchor_link($link) { $pos = strpos($link, '#'); if ( $pos !== false ) { // anchor link found $link_without_anchor = substr($link, 0, $pos); $anchor_link = substr($link, $pos + 1); return array($link_without_anchor, $anchor_link); } else { // anchor link not found return array($link, ''); } } /** * Copy a remote file * in replacement of the copy function * * @param string $url URL of the source file * @param string $path destination file * @return boolean */ public function remote_copy($url, $path) { $result = false; if ( !$this->plugin_options['force_media_import'] && file_exists($path) && (filesize($path) > 0) ) { // Don't download the file if already downloaded return true; } $response = wp_remote_get($url, array( 'timeout' => $this->plugin_options['timeout'], 'sslverify' => false, 'user-agent' => 'Mozilla/5.0 AppleWebKit (KHTML, like Gecko) Chrome/ Safari/', // the default "WordPress..." user agent is rejected with some NGINX config )); // Uses WordPress HTTP API if ( is_wp_error($response) ) { trigger_error($response->get_error_message(), E_USER_WARNING); } elseif ( $response['response']['code'] != 200 ) { trigger_error($response['response']['message'], E_USER_WARNING); } else { $content_type = wp_remote_retrieve_header($response, 'content-type'); if ( preg_match('/^text/', $content_type) ) { // Not a media trigger_error('Not a media', E_USER_WARNING); } else { file_put_contents($path, wp_remote_retrieve_body($response)); $result = true; } } return $result; } /** * Recount the items for a taxonomy * * @return boolean */ private function terms_tax_count($taxonomy) { $terms = get_terms(array($taxonomy)); // Get the term taxonomies $terms_taxonomies = array(); foreach ( $terms as $term ) { $terms_taxonomies[] = $term->term_taxonomy_id; } if ( !empty($terms_taxonomies) ) { return wp_update_term_count_now($terms_taxonomies, $taxonomy); } else { return true; } } /** * Recount the items for each category and tag * * @return boolean */ private function terms_count() { $result = $this->terms_tax_count('category'); $result |= $this->terms_tax_count('post_tag'); } /** * Returns the imported posts mapped with their Spip ID * * @return array of post IDs [spip_article_id => wordpress_post_id] */ public function get_imported_spip_articles() { global $wpdb; $posts = array(); $sql = "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = '_fgs2wp_old_article_id'"; $results = $wpdb->get_results($sql); foreach ( $results as $result ) { $posts[$result->meta_value] = $result->post_id; } ksort($posts); return $posts; } /** * Returns the imported posts mapped with their Spip ID * * @return array of post IDs [spip_news_id => wordpress_post_id] */ public function get_imported_spip_news() { global $wpdb; $posts = array(); $sql = "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = '_fgs2wp_old_news_id'"; $results = $wpdb->get_results($sql); foreach ( $results as $result ) { $posts[$result->meta_value] = $result->post_id; } ksort($posts); return $posts; } /** * Returns the imported users mapped with their Spip ID * * @return array of user IDs [spip_user_id => wordpress_user_id] */ public function get_imported_spip_users() { global $wpdb; $users = array(); $sql = "SELECT user_id, meta_value FROM {$wpdb->usermeta} WHERE meta_key = '_fgs2wp_old_user_id'"; $results = $wpdb->get_results($sql); foreach ( $results as $result ) { $users[$result->meta_value] = $result->user_id; } ksort($users); return $users; } /** * Test if a column exists * * @param string $table Table name * @param string $column Column name * @return bool */ public function column_exists($table, $column) { global $spip_db; $cache_key = 'fgs2wp_column_exists:' . $table . '.' . $column; $found = false; $column_exists = wp_cache_get($cache_key, '', false, $found); if ( $found === false ) { $column_exists = false; try { $prefix = $this->plugin_options['prefix']; switch ( $this->plugin_options['driver'] ) { case 'mysql': $sql = "SHOW COLUMNS FROM ${prefix}${table} LIKE '$column'"; break; case 'sqlite': $sql = "PRAGMA table_info(${prefix}${table})"; break; } $query = $spip_db->query($sql, PDO::FETCH_ASSOC); if ( $query !== false ) { if ( $this->plugin_options['driver'] == 'sqlite' ) { // SQLite $result = $query->fetchAll(); foreach ( $result as $row ) { if ( isset($row['name']) && ($row['name'] == $column) ) { $column_exists = true; break; } } } else { // MySQL $result = $query->fetch(); $column_exists = !empty($result); } } } catch ( PDOException $e ) {} // Store the result in cache for the current request wp_cache_set($cache_key, $column_exists); } return $column_exists; } /** * Test if a table exists * * @param string $table Table name * @return bool */ public function table_exists($table) { global $spip_db; $cache_key = 'fgs2wp_table_exists:' . $table; $found = false; $table_exists = wp_cache_get($cache_key, '', false, $found); if ( $found === false ) { $table_exists = false; try { $prefix = $this->plugin_options['prefix']; switch ( $this->plugin_options['driver'] ) { case 'mysql': $sql = "SHOW TABLES LIKE '${prefix}${table}'"; break; case 'sqlite': $sql = "SELECT name FROM sqlite_master WHERE type='table' AND name='${prefix}${table}';"; break; } $query = $spip_db->query($sql, PDO::FETCH_ASSOC); if ( $query !== false ) { $result = $query->fetch(); $table_exists = !empty($result); } } catch ( PDOException $e ) {} // Store the result in cache for the current request wp_cache_set($cache_key, $table_exists); } return $table_exists; } /** * Get all the term metas corresponding to a meta key * * @param string $meta_key Meta key * @return array List of term metas: term_id => meta_value */ public function get_term_metas_by_metakey($meta_key) { global $wpdb; $metas = array(); $sql = "SELECT term_id, meta_value FROM {$wpdb->termmeta} WHERE meta_key = '$meta_key'"; $results = $wpdb->get_results($sql); foreach ( $results as $result ) { $metas[$result->meta_value] = $result->term_id; } ksort($metas); return $metas; } /** * Returns the imported post ID corresponding to a SPIP article ID * * @param int $spip_id SPIP article ID * @return int WordPress post ID */ public function get_wp_post_id_from_spip_article_id($spip_id) { $post_id = $this->get_wp_post_id_from_meta('_fgs2wp_old_article_id', $spip_id); return $post_id; } /** * Returns the imported post ID corresponding to a SPIP news ID * * @param int $spip_id SPIP news ID * @return int WordPress post ID */ public function get_wp_post_id_from_spip_news_id($spip_id) { $post_id = $this->get_wp_post_id_from_meta('_fgs2wp_old_news_id', $spip_id); return $post_id; } /** * Returns the imported post ID corresponding to a meta key and value * * @param string $meta_key Meta key * @param string $meta_value Meta value * @return int WordPress post ID */ public function get_wp_post_id_from_meta($meta_key, $meta_value) { global $wpdb; $sql = "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '$meta_key' AND meta_value = '$meta_value' LIMIT 1"; $post_id = $wpdb->get_var($sql); return $post_id; } /** * Sanitize a date * * @param date $date Date * @return date */ public function sanitize_date($date) { $date = preg_replace('#-00#', '-01', $date); // For the dates having day = 00 return $date; } } }