You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

372 lines
11 KiB

<?php
/**
* Represents a set of posts and other site data to be exported.
*
* An immutable object, which gathers all data needed for the export.
*/
class WP_Export_Query {
const QUERY_CHUNK = 100;
private static $defaults = array(
'post_ids' => null,
'post_type' => null,
'status' => null,
'author' => null,
'start_date' => null,
'end_date' => null,
'start_id' => null,
'category' => null,
);
private $post_ids;
private $filters;
private $xml_gen;
private $wheres = array();
private $joins = array();
private $author;
private $category;
public $missing_parents = false;
public function __construct( $filters = array() ) {
$this->filters = wp_parse_args( $filters, self::$defaults );
$this->post_ids = $this->calculate_post_ids();
}
public function post_ids() {
return $this->post_ids;
}
public function charset() {
return get_bloginfo( 'charset' );
}
public function site_metadata() {
$metadata = array(
'name' => $this->bloginfo_rss( 'name' ),
'url' => $this->bloginfo_rss( 'url' ),
'language' => $this->bloginfo_rss( 'language' ),
'description' => $this->bloginfo_rss( 'description' ),
'pubDate' => date( 'D, d M Y H:i:s +0000' ),
'site_url' => is_multisite()? network_home_url() : $this->bloginfo_rss( 'url' ),
'blog_url' => $this->bloginfo_rss( 'url' ),
);
return $metadata;
}
public function wp_generator_tag() {
return apply_filters( 'the_generator', get_the_generator( 'export' ), 'export' );
}
public function authors() {
global $wpdb;
$authors = array();
$author_ids = $wpdb->get_col( "SELECT DISTINCT post_author FROM $wpdb->posts WHERE post_status != 'auto-draft'" );
foreach ( (array) $author_ids as $author_id ) {
$authors[] = get_userdata( $author_id );
}
$authors = array_filter( $authors );
return $authors;
}
public function categories() {
if ( $this->category ) {
return array( $this->category );
}
if ( $this->filters['post_type'] ) {
return array();
}
$categories = (array) get_categories( array( 'get' => 'all' ) );
$this->check_for_orphaned_terms( $categories );
$categories = self::topologically_sort_terms( $categories );
return $categories;
}
public function tags() {
if ( $this->filters['post_type'] ) {
return array();
}
$tags = (array) get_tags( array( 'get' => 'all' ) );
$this->check_for_orphaned_terms( $tags );
return $tags;
}
public function custom_taxonomies_terms() {
if ( $this->filters['post_type'] ) {
return array();
}
$custom_taxonomies = get_taxonomies( array( '_builtin' => false ) );
$custom_terms = (array) get_terms( $custom_taxonomies, array( 'get' => 'all' ) );
$this->check_for_orphaned_terms( $custom_terms );
$custom_terms = self::topologically_sort_terms( $custom_terms );
return $custom_terms;
}
public function nav_menu_terms() {
$nav_menus = wp_get_nav_menus();
foreach( $nav_menus as &$term ) {
$term->description = '';
}
return $nav_menus;
}
public function exportify_post( $post ) {
$GLOBALS['wp_query']->in_the_loop = true;
$previous_global_post = \WP_CLI\Utils\get_flag_value( $GLOBALS, 'post' );
$GLOBALS['post'] = $post;
setup_postdata( $post );
$post->post_content = apply_filters( 'the_content_export', $post->post_content );
$post->post_excerpt = apply_filters( 'the_excerpt_export', $post->post_excerpt );
$post->is_sticky = is_sticky( $post->ID ) ? 1 : 0;
$post->terms = self::get_terms_for_post( $post );
$post->meta = self::get_meta_for_post( $post );
$post->comments = self::get_comments_for_post( $post );
$GLOBALS['post'] = $previous_global_post;
return $post;
}
public function posts() {
$posts_iterator = new WP_Post_IDs_Iterator( $this->post_ids, self::QUERY_CHUNK );
return new WP_Map_Iterator( $posts_iterator, array( $this, 'exportify_post' ) );
}
private function calculate_post_ids() {
global $wpdb;
if ( is_array( $this->filters['post_ids'] ) ) {
return $this->filters['post_ids'];
}
$this->post_type_where();
$this->status_where();
$this->author_where();
$this->start_date_where();
$this->end_date_where();
$this->start_id_where();
$this->category_where();
$where = implode( ' AND ', array_filter( $this->wheres ) );
if ( $where ) $where = "WHERE $where";
$join = implode( ' ', array_filter( $this->joins ) );
$post_ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} AS p $join $where" );
$post_ids = array_merge( $post_ids, $this->attachments_for_specific_post_types( $post_ids ) );
return $post_ids;
}
private function post_type_where() {
global $wpdb;
$post_types_filters = array( 'can_export' => true );
if ( $this->filters['post_type'] ) {
$post_types = $this->filters['post_type'];
// Flatten single post types
if ( is_array( $post_types ) && 1 === count( $post_types ) ) {
$post_types = array_shift( $post_types );
}
$post_types_filters = array_merge( $post_types_filters, array( 'name' => $post_types ) );
}
// Multiple post types
if ( is_array( $post_types_filters['name'] ) ) {
$post_types = array();
foreach ( $post_types_filters['name'] as $post_type ) {
if ( post_type_exists( $post_type ) ) {
$post_types[] = $post_type;
}
}
} else {
$post_types = get_post_types( $post_types_filters );
}
if ( ! $post_types ) {
$this->wheres[] = 'p.post_type IS NULL';
return;
}
$this->wheres[] = _wp_export_build_IN_condition( 'p.post_type', $post_types );
}
private function status_where() {
global $wpdb;
if ( !$this->filters['status'] ) {
$this->wheres[] = "p.post_status != 'auto-draft'";
return;
}
$this->wheres[] = $wpdb->prepare( 'p.post_status = %s', $this->filters['status'] );
}
private function author_where() {
global $wpdb;
$user = $this->find_user_from_any_object( $this->filters['author'] );
if ( !$user || is_wp_error( $user ) ) {
return;
}
$this->author = $user;
$this->wheres[] = $wpdb->prepare( 'p.post_author = %d', $user->ID );
}
private function start_date_where() {
global $wpdb;
$timestamp = strtotime( $this->filters['start_date'] );
if ( !$timestamp ) {
return;
}
$this->wheres[] = $wpdb->prepare( 'p.post_date >= %s', date( 'Y-m-d 00:00:00', $timestamp ) );
}
private function end_date_where() {
global $wpdb;
if ( preg_match( '/^\d{4}-\d{2}$/', $this->filters['end_date'] ) ) {
$timestamp = $this->get_timestamp_for_the_last_day_of_a_month( $this->filters['end_date'] );
} else {
$timestamp = strtotime( $this->filters['end_date'] );
}
if ( !$timestamp ) {
return;
}
$this->wheres[] = $wpdb->prepare( 'p.post_date <= %s', date( 'Y-m-d 23:59:59', $timestamp ) );
}
private function start_id_where() {
global $wpdb;
$start_id = absint( $this->filters['start_id'] );
if ( 0 === $start_id ) {
return;
}
$this->wheres[] = $wpdb->prepare( 'p.ID >= %d', $start_id );
}
private function get_timestamp_for_the_last_day_of_a_month( $yyyy_mm ) {
return strtotime( "$yyyy_mm +1month -1day" );
}
private function category_where() {
global $wpdb;
if ( 'post' != $this->filters['post_type'] && ! in_array( 'post', (array) $this->filters['post_type'] ) ) {
return;
}
$category = $this->find_category_from_any_object( $this->filters['category'] );
if ( !$category ) {
return;
}
$this->category = $category;
$this->joins[] = "INNER JOIN {$wpdb->term_relationships} AS tr ON (p.ID = tr.object_id)";
$this->wheres[] = $wpdb->prepare( 'tr.term_taxonomy_id = %d', $category->term_taxonomy_id );
}
private function attachments_for_specific_post_types( $post_ids ) {
global $wpdb;
if ( !$this->filters['post_type'] ) {
return array();
}
$attachment_ids = array();
while ( $batch_of_post_ids = array_splice( $post_ids, 0, self::QUERY_CHUNK ) ) {
$post_parent_condition = _wp_export_build_IN_condition( 'post_parent', $batch_of_post_ids );
$attachment_ids = array_merge( $attachment_ids, (array)$wpdb->get_col( "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'attachment' AND $post_parent_condition" ) );
}
return array_map( 'intval', $attachment_ids );
}
private function bloginfo_rss( $section ) {
return apply_filters( 'bloginfo_rss', get_bloginfo_rss( $section ), $section );
}
private function find_user_from_any_object( $user ) {
if ( is_numeric( $user ) ) {
return get_user_by( 'id', $user );
} elseif ( is_string( $user ) ) {
return get_user_by( 'login', $user );
} elseif ( isset( $user->ID ) ) {
return get_user_by( 'id', $user->ID );
}
return false;
}
private function find_category_from_any_object( $category ) {
if ( is_numeric( $category ) ) {
return get_term( $category, 'category' );
} elseif ( is_string( $category ) ) {
$term = term_exists( $category, 'category' );
return isset( $term['term_id'] )? get_term( $term['term_id'], 'category' ) : false;
} elseif ( isset( $category->term_id ) ) {
return get_term( $category->term_id, 'category' );
}
return false;
}
private static function topologically_sort_terms( $terms ) {
$sorted = array();
while ( $term = array_shift( $terms ) ) {
if ( $term->parent == 0 || isset( $sorted[$term->parent] ) )
$sorted[$term->term_id] = $term;
else
$terms[] = $term;
}
return $sorted;
}
private function check_for_orphaned_terms( $terms ) {
$term_ids = array();
$have_parent = array();
foreach ( $terms as $term ) {
$term_ids[ $term->term_id ] = true;
if ( $term->parent != 0 )
$have_parent[] = $term;
}
foreach ( $have_parent as $has_parent ) {
if ( ! isset( $term_ids[ $has_parent->parent ] ) ) {
$this->missing_parents = $has_parent;
throw new WP_Export_Term_Exception( sprintf( __( 'Term is missing a parent: %s (%d)' ), $has_parent->slug, $has_parent->term_taxonomy_id ) );
}
}
}
private static function get_terms_for_post( $post ) {
$taxonomies = get_object_taxonomies( $post->post_type );
if ( empty( $taxonomies ) )
return array();
$terms = wp_get_object_terms( $post->ID, $taxonomies );
$terms = $terms? $terms : array();
return $terms;
}
private static function get_meta_for_post( $post ) {
global $wpdb;
$meta_for_export = array();
$meta_from_db = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID ) );
foreach ( $meta_from_db as $meta ) {
if ( apply_filters( 'wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) )
continue;
if ( in_array( $meta->meta_key, array( '_edit_lock', '_wp_attachment_metadata', '_wp_attached_file' ) ) ) {
continue;
}
$meta_for_export[] = $meta;
}
return $meta_for_export;
}
private static function get_comments_for_post( $post ) {
global $wpdb;
$comments = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved <> 'spam'", $post->ID ) );
foreach( $comments as $comment ) {
$meta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $comment->comment_ID ) );
$meta = $meta? $meta : array();
$comment->meta = $meta;
}
return $comments;
}
}
class WP_Export_Exception extends RuntimeException {
}
class WP_Export_Term_Exception extends RuntimeException {
}