Skip to content

Commit

Permalink
Add a Jetpack/WordPress.com importer. Please use responsibly.
Browse files Browse the repository at this point in the history
  • Loading branch information
Beau Lebens committed Apr 15, 2017
1 parent fdf1834 commit 090030b
Showing 1 changed file with 280 additions and 0 deletions.
280 changes: 280 additions & 0 deletions importers/keyring-importer-jetpack.php
@@ -0,0 +1,280 @@
<?php

// This is a horrible hack, because WordPress doesn't support dependencies/load-order.
// We wrap our entire class definition in a function, and then only call that on a hook
// where we know that the class we're extending is available. *hangs head in shame*
function Keyring_Jetpack_Importer() {


class Keyring_Jetpack_Importer extends Keyring_Importer_Base {
const SLUG = 'jetpack'; // e.g. 'twitter' (should match a service in Keyring)
const LABEL = 'Jetpack'; // e.g. 'Twitter'
const KEYRING_SERVICE = 'Keyring_Service_Jetpack'; // Full class name of the Keyring_Service this importer requires
const REQUESTS_PER_LOAD = 3; // How many remote requests should be made before reloading the page when interactively importing?
const POSTS_PER_REQUEST = 25; // How many posts should we ask for in each request?
function __construct() {
parent::__construct();
add_action( 'keyring_importer_jetpack_custom_options', array( $this, 'custom_options' ) );
add_filter( 'wp_head', array( $this, 'wp_head' ), 1 );
}

// Don't index single-post pages for imported posts, to avoid duplicate content issues
function wp_head() {
if ( ! is_single() ) {
return;
}

if ( has_term( self::SLUG, 'keyring_services', get_queried_object() ) ) {
echo '<meta name="robots" content="noindex,follow" >';
}
}

function custom_options() {
?><tr valign="top">
<th scope="row">
<label for="site"><?php _e( 'Import posts from', 'keyring' ); ?></label>
</th>
<td>
<select name="site">
<?php
// Get the site list for this user, and display it with the selected one marked
$response = $this->service->request(
'https://public-api.wordpress.com/rest/v1.1/me/sites',
array(
'timeout' => 60 // Required for accounts with a ton of sites
)
);
if ( Keyring_Util::is_error( $response ) ) {
$meta = array();
} else {
foreach ( $response->sites as $site ) {
echo '<option value="' . esc_attr( $site->ID ) . '"' . selected( $site->ID, $this->get_option( 'site' ), false ) . '>' . esc_attr( $site->name ) . ' (' . esc_attr( $site->URL ) . ')</option>';
}
}
?>
</select>
</td>
</tr><?php
}

function handle_request_options() {
// Validate options and store them so they can be used in auto-imports

if ( empty( $_POST['site'] ) || ! ctype_digit( $_POST['site'] ) ) {
$this->error( __( "You must select a site from which to import posts." ) );
}

if ( empty( $_POST['category'] ) || ! ctype_digit( $_POST['category'] ) ) {
$this->error( __( "Make sure you select a valid category to import your links into." ) );
}

if ( empty( $_POST['author'] ) || ! ctype_digit( $_POST['author'] ) ) {
$this->error( __( "You must select an author to assign to all imported links." ) );
}

if ( isset( $_POST['auto_import'] ) ) {
$_POST['auto_import'] = true;
} else {
$_POST['auto_import'] = false;
}

// If there were errors, output them, otherwise store options and start importing
if ( count( $this->errors ) ) {
$this->step = 'options';
} else {
$this->set_option( array(
'site' => (int) $_POST['site'],
'category' => (int) $_POST['category'],
'tags' => explode( ',', $_POST['tags'] ),
'author' => (int) $_POST['author'],
'auto_import' => $_POST['auto_import'],
) );

$this->step = 'import';
}
}

function build_request_url() {
// Get posts for the site we're importing from
$url = sprintf( "https://public-api.wordpress.com/rest/v1.1/sites/%s/posts/", $this->get_option( 'site' ) );
$url = add_query_arg(
array(
'number' => self::POSTS_PER_REQUEST,
),
$url
);

if ( $this->auto_import ) {
// Get most recent post we've imported (if any), and its date so that we can get new ones since then
$latest = get_posts( array(
'numberposts' => 1,
'orderby' => 'date',
'order' => 'DESC',
'tax_query' => array( array(
'taxonomy' => 'keyring_services',
'field' => 'slug',
'terms' => array( $this->taxonomy->slug ),
'operator' => 'IN',
) ),
) );

// If we have already imported some, then start since the most recent
if ( $latest ) {
$url = add_query_arg(
array(
'after' => urlencode( date( 'Y-m-d H:i:s', strtotime( $latest[0]->post_date_gmt ) + 1 ) ),
'order_by' => 'date',
'order' => 'ASC',
),
$url
);
}
} else {
// Handle page offsets (only for non-auto-import requests)
$url = add_query_arg(
array(
'page' => $this->get_option( 'page', 1 ),
'order_by' => 'date',
'order' => 'DESC',
),
$url
);
}

return $url;
}

function extract_posts_from_data( $raw ) {
$importdata = $raw;

if ( null === $importdata ) {
$this->finished = true;
return new Keyring_Error( 'keyring-jetpack-importer-failed-download', __( 'Failed to download or parse posts from WordPress.com. Please wait a few minutes and try again.' ) );
}

// .found should be there
if ( ! property_exists( $importdata, 'found' ) || ! property_exists( $importdata, 'posts' ) ) {
$this->finished = true;
return new Keyring_Error( 'keyring-jetpack-importer-no-posts', __( 'No posts were found for the site.' ) );
}

// if .found is 0, then we have no posts, we're done
if ( 0 == $importdata->found || empty( $importdata->posts ) ) {
$this->finished = true;
return;
}

// Parse/convert everything to WP post structs
foreach ( $importdata->posts as $post ) {

$post_title = $post->title;

// Parse/adjust dates
$post_date_gmt = date( 'Y-m-d H:i:s', strtotime( $post->date ) );
$post_date = get_date_from_gmt( $post_date_gmt );

// Apply selected category (ignores the categories from the original post)
$post_category = array( $this->get_option( 'category' ) );

// Get the default/forced ones, and merge with tags from the post
$tags = array_merge( $this->get_option( 'tags' ), array_keys( (array) $post->tags ) );

$href = $post->URL;
$post_content = $post->content;
$post_excerpt = $post->excerpt;

// Other bits
$post_author = $this->get_option( 'author' );
$post_status = 'publish';
$post_format = $post->format;
$jetpack_id = $post->global_ID;
$jetpack_raw = $post;

// Build the post array, and hang onto it along with the others
$this->posts[] = compact(
'post_author',
'post_date',
'post_date_gmt',
'post_content',
'post_excerpt',
'post_title',
'post_status',
'post_format',
'post_category',
'tags',
'href',
'jetpack_id',
'jetpack_raw'
);
}
}

function insert_posts() {
global $wpdb;
$imported = 0;
$skipped = 0;
foreach ( $this->posts as $post ) {
extract( $post );
if (
! $jetpack_id
||
$wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM {$wpdb->postmeta} WHERE meta_key = 'jetpack_id' AND meta_value = %s", $jetpack_id ) )
||
$post_id = post_exists( $post_title, $post_content, $post_date )
) {
// Looks like a duplicate, which means we've already processed it
$skipped++;
} else {
$post_id = wp_insert_post( $post );

if ( is_wp_error( $post_id ) ) {
return $post_id;
}

if ( ! $post_id ) {
continue;
}

// Track which Keyring service was used
wp_set_object_terms( $post_id, self::LABEL, 'keyring_services' );

// Assign post format based on what it was originally
set_post_format( $post_id, $post_format );

// Update Category
wp_set_post_categories( $post_id, $post_category );

add_post_meta( $post_id, 'jetpack_id', $jetpack_id );
add_post_meta( $post_id, 'href', $href );

if ( count( $tags ) ) {
wp_set_post_terms( $post_id, implode( ',', $tags ) );
}

add_post_meta( $post_id, 'raw_import_data', wp_slash( json_encode( $jetpack_raw ) ) );

$imported++;

do_action( 'keyring_post_imported', $post_id, static::SLUG, $post );
}
}
$this->posts = array();

// Return, so that the handler can output info (or update DB, or whatever)
return array( 'imported' => $imported, 'skipped' => $skipped );
}
}

} // end function Keyring_Jetpack_Importer


add_action( 'init', function() {
Keyring_Jetpack_Importer(); // Load the class code from above
keyring_register_importer(
'jetpack',
'Keyring_Jetpack_Importer',
plugin_basename( __FILE__ ),
__( 'Import posts from a Jetpack-powered WordPress site (WordPress.com or self-hosted), using the Jetpack REST API.', 'keyring' )
);
} );

0 comments on commit 090030b

Please sign in to comment.