Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 090030b

Browse files
author
Beau Lebens
committedApr 15, 2017
Add a Jetpack/WordPress.com importer. Please use responsibly.
1 parent fdf1834 commit 090030b

File tree

1 file changed

+280
-0
lines changed

1 file changed

+280
-0
lines changed
 
+280
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
<?php
2+
3+
// This is a horrible hack, because WordPress doesn't support dependencies/load-order.
4+
// We wrap our entire class definition in a function, and then only call that on a hook
5+
// where we know that the class we're extending is available. *hangs head in shame*
6+
function Keyring_Jetpack_Importer() {
7+
8+
9+
class Keyring_Jetpack_Importer extends Keyring_Importer_Base {
10+
const SLUG = 'jetpack'; // e.g. 'twitter' (should match a service in Keyring)
11+
const LABEL = 'Jetpack'; // e.g. 'Twitter'
12+
const KEYRING_SERVICE = 'Keyring_Service_Jetpack'; // Full class name of the Keyring_Service this importer requires
13+
const REQUESTS_PER_LOAD = 3; // How many remote requests should be made before reloading the page when interactively importing?
14+
const POSTS_PER_REQUEST = 25; // How many posts should we ask for in each request?
15+
16+
function __construct() {
17+
parent::__construct();
18+
add_action( 'keyring_importer_jetpack_custom_options', array( $this, 'custom_options' ) );
19+
add_filter( 'wp_head', array( $this, 'wp_head' ), 1 );
20+
}
21+
22+
// Don't index single-post pages for imported posts, to avoid duplicate content issues
23+
function wp_head() {
24+
if ( ! is_single() ) {
25+
return;
26+
}
27+
28+
if ( has_term( self::SLUG, 'keyring_services', get_queried_object() ) ) {
29+
echo '<meta name="robots" content="noindex,follow" >';
30+
}
31+
}
32+
33+
function custom_options() {
34+
?><tr valign="top">
35+
<th scope="row">
36+
<label for="site"><?php _e( 'Import posts from', 'keyring' ); ?></label>
37+
</th>
38+
<td>
39+
<select name="site">
40+
<?php
41+
// Get the site list for this user, and display it with the selected one marked
42+
$response = $this->service->request(
43+
'https://public-api.wordpress.com/rest/v1.1/me/sites',
44+
array(
45+
'timeout' => 60 // Required for accounts with a ton of sites
46+
)
47+
);
48+
if ( Keyring_Util::is_error( $response ) ) {
49+
$meta = array();
50+
} else {
51+
foreach ( $response->sites as $site ) {
52+
echo '<option value="' . esc_attr( $site->ID ) . '"' . selected( $site->ID, $this->get_option( 'site' ), false ) . '>' . esc_attr( $site->name ) . ' (' . esc_attr( $site->URL ) . ')</option>';
53+
}
54+
}
55+
?>
56+
</select>
57+
</td>
58+
</tr><?php
59+
}
60+
61+
function handle_request_options() {
62+
// Validate options and store them so they can be used in auto-imports
63+
64+
if ( empty( $_POST['site'] ) || ! ctype_digit( $_POST['site'] ) ) {
65+
$this->error( __( "You must select a site from which to import posts." ) );
66+
}
67+
68+
if ( empty( $_POST['category'] ) || ! ctype_digit( $_POST['category'] ) ) {
69+
$this->error( __( "Make sure you select a valid category to import your links into." ) );
70+
}
71+
72+
if ( empty( $_POST['author'] ) || ! ctype_digit( $_POST['author'] ) ) {
73+
$this->error( __( "You must select an author to assign to all imported links." ) );
74+
}
75+
76+
if ( isset( $_POST['auto_import'] ) ) {
77+
$_POST['auto_import'] = true;
78+
} else {
79+
$_POST['auto_import'] = false;
80+
}
81+
82+
// If there were errors, output them, otherwise store options and start importing
83+
if ( count( $this->errors ) ) {
84+
$this->step = 'options';
85+
} else {
86+
$this->set_option( array(
87+
'site' => (int) $_POST['site'],
88+
'category' => (int) $_POST['category'],
89+
'tags' => explode( ',', $_POST['tags'] ),
90+
'author' => (int) $_POST['author'],
91+
'auto_import' => $_POST['auto_import'],
92+
) );
93+
94+
$this->step = 'import';
95+
}
96+
}
97+
98+
function build_request_url() {
99+
// Get posts for the site we're importing from
100+
$url = sprintf( "https://public-api.wordpress.com/rest/v1.1/sites/%s/posts/", $this->get_option( 'site' ) );
101+
$url = add_query_arg(
102+
array(
103+
'number' => self::POSTS_PER_REQUEST,
104+
),
105+
$url
106+
);
107+
108+
if ( $this->auto_import ) {
109+
// Get most recent post we've imported (if any), and its date so that we can get new ones since then
110+
$latest = get_posts( array(
111+
'numberposts' => 1,
112+
'orderby' => 'date',
113+
'order' => 'DESC',
114+
'tax_query' => array( array(
115+
'taxonomy' => 'keyring_services',
116+
'field' => 'slug',
117+
'terms' => array( $this->taxonomy->slug ),
118+
'operator' => 'IN',
119+
) ),
120+
) );
121+
122+
// If we have already imported some, then start since the most recent
123+
if ( $latest ) {
124+
$url = add_query_arg(
125+
array(
126+
'after' => urlencode( date( 'Y-m-d H:i:s', strtotime( $latest[0]->post_date_gmt ) + 1 ) ),
127+
'order_by' => 'date',
128+
'order' => 'ASC',
129+
),
130+
$url
131+
);
132+
}
133+
} else {
134+
// Handle page offsets (only for non-auto-import requests)
135+
$url = add_query_arg(
136+
array(
137+
'page' => $this->get_option( 'page', 1 ),
138+
'order_by' => 'date',
139+
'order' => 'DESC',
140+
),
141+
$url
142+
);
143+
}
144+
145+
return $url;
146+
}
147+
148+
function extract_posts_from_data( $raw ) {
149+
$importdata = $raw;
150+
151+
if ( null === $importdata ) {
152+
$this->finished = true;
153+
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.' ) );
154+
}
155+
156+
// .found should be there
157+
if ( ! property_exists( $importdata, 'found' ) || ! property_exists( $importdata, 'posts' ) ) {
158+
$this->finished = true;
159+
return new Keyring_Error( 'keyring-jetpack-importer-no-posts', __( 'No posts were found for the site.' ) );
160+
}
161+
162+
// if .found is 0, then we have no posts, we're done
163+
if ( 0 == $importdata->found || empty( $importdata->posts ) ) {
164+
$this->finished = true;
165+
return;
166+
}
167+
168+
// Parse/convert everything to WP post structs
169+
foreach ( $importdata->posts as $post ) {
170+
171+
$post_title = $post->title;
172+
173+
// Parse/adjust dates
174+
$post_date_gmt = date( 'Y-m-d H:i:s', strtotime( $post->date ) );
175+
$post_date = get_date_from_gmt( $post_date_gmt );
176+
177+
// Apply selected category (ignores the categories from the original post)
178+
$post_category = array( $this->get_option( 'category' ) );
179+
180+
// Get the default/forced ones, and merge with tags from the post
181+
$tags = array_merge( $this->get_option( 'tags' ), array_keys( (array) $post->tags ) );
182+
183+
$href = $post->URL;
184+
$post_content = $post->content;
185+
$post_excerpt = $post->excerpt;
186+
187+
// Other bits
188+
$post_author = $this->get_option( 'author' );
189+
$post_status = 'publish';
190+
$post_format = $post->format;
191+
$jetpack_id = $post->global_ID;
192+
$jetpack_raw = $post;
193+
194+
// Build the post array, and hang onto it along with the others
195+
$this->posts[] = compact(
196+
'post_author',
197+
'post_date',
198+
'post_date_gmt',
199+
'post_content',
200+
'post_excerpt',
201+
'post_title',
202+
'post_status',
203+
'post_format',
204+
'post_category',
205+
'tags',
206+
'href',
207+
'jetpack_id',
208+
'jetpack_raw'
209+
);
210+
}
211+
}
212+
213+
function insert_posts() {
214+
global $wpdb;
215+
$imported = 0;
216+
$skipped = 0;
217+
foreach ( $this->posts as $post ) {
218+
extract( $post );
219+
if (
220+
! $jetpack_id
221+
||
222+
$wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM {$wpdb->postmeta} WHERE meta_key = 'jetpack_id' AND meta_value = %s", $jetpack_id ) )
223+
||
224+
$post_id = post_exists( $post_title, $post_content, $post_date )
225+
) {
226+
// Looks like a duplicate, which means we've already processed it
227+
$skipped++;
228+
} else {
229+
$post_id = wp_insert_post( $post );
230+
231+
if ( is_wp_error( $post_id ) ) {
232+
return $post_id;
233+
}
234+
235+
if ( ! $post_id ) {
236+
continue;
237+
}
238+
239+
// Track which Keyring service was used
240+
wp_set_object_terms( $post_id, self::LABEL, 'keyring_services' );
241+
242+
// Assign post format based on what it was originally
243+
set_post_format( $post_id, $post_format );
244+
245+
// Update Category
246+
wp_set_post_categories( $post_id, $post_category );
247+
248+
add_post_meta( $post_id, 'jetpack_id', $jetpack_id );
249+
add_post_meta( $post_id, 'href', $href );
250+
251+
if ( count( $tags ) ) {
252+
wp_set_post_terms( $post_id, implode( ',', $tags ) );
253+
}
254+
255+
add_post_meta( $post_id, 'raw_import_data', wp_slash( json_encode( $jetpack_raw ) ) );
256+
257+
$imported++;
258+
259+
do_action( 'keyring_post_imported', $post_id, static::SLUG, $post );
260+
}
261+
}
262+
$this->posts = array();
263+
264+
// Return, so that the handler can output info (or update DB, or whatever)
265+
return array( 'imported' => $imported, 'skipped' => $skipped );
266+
}
267+
}
268+
269+
} // end function Keyring_Jetpack_Importer
270+
271+
272+
add_action( 'init', function() {
273+
Keyring_Jetpack_Importer(); // Load the class code from above
274+
keyring_register_importer(
275+
'jetpack',
276+
'Keyring_Jetpack_Importer',
277+
plugin_basename( __FILE__ ),
278+
__( 'Import posts from a Jetpack-powered WordPress site (WordPress.com or self-hosted), using the Jetpack REST API.', 'keyring' )
279+
);
280+
} );

0 commit comments

Comments
 (0)
Please sign in to comment.