From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from pigeon.gentoo.org ([208.92.234.80] helo=lists.gentoo.org) by finch.gentoo.org with esmtp (Exim 4.60) (envelope-from ) id 1PxSpp-0005up-IN for garchives@archives.gentoo.org; Wed, 09 Mar 2011 23:30:33 +0000 Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id DB0321C029; Wed, 9 Mar 2011 23:30:21 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) by pigeon.gentoo.org (Postfix) with ESMTP id 59F441C029 for ; Wed, 9 Mar 2011 23:30:21 +0000 (UTC) Received: from pelican.gentoo.org (unknown [66.219.59.40]) (using TLSv1 with cipher ADH-CAMELLIA256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 713551B40DE for ; Wed, 9 Mar 2011 23:30:20 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by pelican.gentoo.org (Postfix) with ESMTP id AC8E88006A for ; Wed, 9 Mar 2011 23:30:19 +0000 (UTC) From: "Theo Chatzimichos" To: gentoo-commits@lists.gentoo.org Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Theo Chatzimichos" Message-ID: <3ed191998d4c870d0a39fbe96ecf0b4eba4dc396.tampakrap@gentoo> Subject: [gentoo-commits] proj/blogs-gentoo:master commit in: plugins/wordpress-importer/, plugins/wordpress-importer/languages/ X-VCS-Repository: proj/blogs-gentoo X-VCS-Files: plugins/wordpress-importer/languages/wordpress-importer.pot plugins/wordpress-importer/parsers.php plugins/wordpress-importer/readme.txt plugins/wordpress-importer/wordpress-importer.php X-VCS-Directories: plugins/wordpress-importer/ plugins/wordpress-importer/languages/ X-VCS-Committer: tampakrap X-VCS-Committer-Name: Theo Chatzimichos X-VCS-Revision: 3ed191998d4c870d0a39fbe96ecf0b4eba4dc396 Date: Wed, 9 Mar 2011 23:30:19 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: quoted-printable X-Archives-Salt: X-Archives-Hash: a6cae9292f854fd05ef4b94b1ca10d46 commit: 3ed191998d4c870d0a39fbe96ecf0b4eba4dc396 Author: Theo Chatzimichos gentoo org> AuthorDate: Wed Mar 9 23:30:01 2011 +0000 Commit: Theo Chatzimichos gentoo org> CommitDate: Wed Mar 9 23:30:01 2011 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=3Dproj/blogs-gentoo.= git;a=3Dcommit;h=3D3ed19199 Add wordpress importer --- .../languages/wordpress-importer.pot | 231 +++++ plugins/wordpress-importer/parsers.php | 601 +++++++++++ plugins/wordpress-importer/readme.txt | 41 + plugins/wordpress-importer/wordpress-importer.php | 1060 ++++++++++++++= ++++++ 4 files changed, 1933 insertions(+), 0 deletions(-) diff --git a/plugins/wordpress-importer/languages/wordpress-importer.pot = b/plugins/wordpress-importer/languages/wordpress-importer.pot new file mode 100644 index 0000000..8dae6cf --- /dev/null +++ b/plugins/wordpress-importer/languages/wordpress-importer.pot @@ -0,0 +1,231 @@ +# Copyright (C) 2011 WordPress Importer +# This file is distributed under the same license as the WordPress Impor= ter package. +msgid "" +msgstr "" +"Project-Id-Version: WordPress Importer 0.3\n" +"Report-Msgid-Bugs-To: http://wordpress.org/tag/wordpress-importer\n" +"POT-Creation-Date: 2011-02-21 21:07:12+00:00\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=3DUTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2010-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" + +#: parsers.php:42 parsers.php:63 +msgid "There was an error when reading this WXR file" +msgstr "" + +#: parsers.php:43 +msgid "" +"Details are shown above. The importer will now try again with a differe= nt " +"parser..." +msgstr "" + +#: parsers.php:67 parsers.php:72 parsers.php:248 parsers.php:430 +msgid "" +"This does not appear to be a WXR file, missing/invalid WXR version numb= er" +msgstr "" + +#: wordpress-importer.php:133 wordpress-importer.php:142 +#: wordpress-importer.php:193 wordpress-importer.php:201 +msgid "Sorry, there has been an error." +msgstr "" + +#: wordpress-importer.php:134 +msgid "The file does not exist, please try again." +msgstr "" + +#: wordpress-importer.php:177 +msgid "All done." +msgstr "" + +#: wordpress-importer.php:177 +msgid "Have fun!" +msgstr "" + +#: wordpress-importer.php:178 +msgid "Remember to update the passwords and roles of imported users." +msgstr "" + +#: wordpress-importer.php:209 +msgid "" +"This WXR file (version %s) may not be supported by this version of the = " +"importer. Please consider updating." +msgstr "" + +#: wordpress-importer.php:234 +msgid "" +"Failed to import author %s. Their posts will be attributed to the curre= nt " +"user." +msgstr "" + +#: wordpress-importer.php:260 +msgid "Assign Authors" +msgstr "" + +#: wordpress-importer.php:261 +msgid "" +"To make it easier for you to edit and save the imported content, you ma= y " +"want to reassign the author of the imported item to an existing user of= this " +"site. For example, you may want to import all the entries as admi= ns entries." +msgstr "" + +#: wordpress-importer.php:263 +msgid "" +"If a new user is created by WordPress, a new password will be randomly = " +"generated and the new user’s role will be set as %s. Manually cha= nging " +"the new user’s details will be necessary." +msgstr "" + +#: wordpress-importer.php:273 +msgid "Import Attachments" +msgstr "" + +#: wordpress-importer.php:276 +msgid "Download and import file attachments" +msgstr "" + +#: wordpress-importer.php:280 +msgid "Submit" +msgstr "" + +#: wordpress-importer.php:293 +msgid "Import author:" +msgstr "" + +#: wordpress-importer.php:304 +msgid "or create new user with login name:" +msgstr "" + +#: wordpress-importer.php:307 +msgid "as a new user:" +msgstr "" + +#: wordpress-importer.php:315 +msgid "assign posts to an existing user:" +msgstr "" + +#: wordpress-importer.php:317 +msgid "or assign posts to an existing user:" +msgstr "" + +#: wordpress-importer.php:318 +msgid "- Select -" +msgstr "" + +#: wordpress-importer.php:366 +msgid "" +"Failed to create new user for %s. Their posts will be attributed to the= " +"current user." +msgstr "" + +#: wordpress-importer.php:413 +msgid "Failed to import category %s" +msgstr "" + +#: wordpress-importer.php:449 +msgid "Failed to import post tag %s" +msgstr "" + +#: wordpress-importer.php:491 wordpress-importer.php:603 +msgid "Failed to import %s %s" +msgstr "" + +#: wordpress-importer.php:513 +msgid "Failed to import “%s”: Invalid post type %s" +msgstr "" + +#: wordpress-importer.php:534 +msgid "%s “%s” already exists." +msgstr "" + +#: wordpress-importer.php:575 +msgid "Failed to import %s “%s”" +msgstr "" + +#: wordpress-importer.php:712 +msgid "Menu item skipped due to missing menu slug" +msgstr "" + +#: wordpress-importer.php:719 +msgid "Menu item skipped due to invalid menu slug: %s" +msgstr "" + +#: wordpress-importer.php:782 +msgid "Fetching attachments is not enabled" +msgstr "" + +#: wordpress-importer.php:795 +msgid "Invalid file type" +msgstr "" + +#: wordpress-importer.php:838 +msgid "Remote server did not respond" +msgstr "" + +#: wordpress-importer.php:844 +msgid "Remote server returned error response %1$d %2$s" +msgstr "" + +#: wordpress-importer.php:851 +msgid "Remote file is incorrect size" +msgstr "" + +#: wordpress-importer.php:856 +msgid "Zero size file downloaded" +msgstr "" + +#: wordpress-importer.php:862 +msgid "Remote file is too large, limit is %s" +msgstr "" + +#: wordpress-importer.php:961 +msgid "Import WordPress" +msgstr "" + +#: wordpress-importer.php:968 +msgid "" +"A new version of this importer is available. Please update to version %= s to " +"ensure compatibility with newer export files." +msgstr "" + +#: wordpress-importer.php:983 +msgid "" +"Howdy! Upload your WordPress eXtended RSS (WXR) file and we’ll im= port " +"the posts, pages, comments, custom fields, categories, and tags into th= is " +"site." +msgstr "" + +#: wordpress-importer.php:984 +msgid "Choose a WXR file to upload, then click Upload file and import." +msgstr "" + +#: wordpress-importer.php:1058 +msgid "" +"Import posts, pages, comments, custom fields, categories, and t= ags from a WordPress export file." +msgstr "" + +#. Plugin Name of the plugin/theme +msgid "WordPress Importer" +msgstr "" + +#. Plugin URI of the plugin/theme +msgid "http://wordpress.org/extend/plugins/wordpress-importer/" +msgstr "" + +#. Description of the plugin/theme +msgid "" +"Import posts, pages, comments, custom fields, categories, tags and more= from " +"a WordPress export file." +msgstr "" + +#. Author of the plugin/theme +msgid "wordpressdotorg" +msgstr "" + +#. Author URI of the plugin/theme +msgid "http://wordpress.org/" +msgstr "" diff --git a/plugins/wordpress-importer/parsers.php b/plugins/wordpress-i= mporter/parsers.php new file mode 100644 index 0000000..1724729 --- /dev/null +++ b/plugins/wordpress-importer/parsers.php @@ -0,0 +1,601 @@ +parse( $file ); + + // If SimpleXML succeeds or this is an invalid WXR file then return t= he results + if ( ! is_wp_error( $result ) || 'SimpleXML_parse_error' !=3D $result= ->get_error_code() ) + return $result; + } else if ( extension_loaded( 'xml' ) ) { + $parser =3D new WXR_Parser_XML; + $result =3D $parser->parse( $file ); + + // If XMLParser succeeds or this is an invalid WXR file then return t= he results + if ( ! is_wp_error( $result ) || 'XML_parse_error' !=3D $result->get_= error_code() ) + return $result; + } + + // We have a malformed XML file, so display the error and fallthrough = to regex + if ( isset($result) && defined('IMPORT_DEBUG') && IMPORT_DEBUG ) { + echo '
';
+			if ( 'SimpleXML_parse_error' =3D=3D $result->get_error_code() ) {
+				foreach  ( $result->get_error_data() as $error )
+					echo $error->line . ':' . $error->column . ' ' . esc_html( $error->=
message ) . "\n";
+			} else if ( 'XML_parse_error' =3D=3D $result->get_error_code() ) {
+				$error =3D $result->get_error_data();
+				echo $error[0] . ':' . $error[1] . ' ' . esc_html( $error[2] );
+			}
+			echo '
'; + echo '

' . __( 'There was an error when reading this WXR fi= le', 'wordpress-importer' ) . '
'; + echo __( 'Details are shown above. The importer will now try again wi= th a different parser...', 'wordpress-importer' ) . '

'; + } + + // use regular expressions if nothing else available or this is bad XM= L + $parser =3D new WXR_Parser_Regex; + return $parser->parse( $file ); + } +} + +/** + * WXR Parser that makes use of the SimpleXML PHP extension. + */ +class WXR_Parser_SimpleXML { + function parse( $file ) { + $authors =3D $posts =3D $categories =3D $tags =3D $terms =3D array(); + + $internal_errors =3D libxml_use_internal_errors(true); + $xml =3D simplexml_load_file( $file ); + // halt if loading produces an error + if ( ! $xml ) + return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error= when reading this WXR file', 'wordpress-importer' ), libxml_get_errors()= ); + + $wxr_version =3D $xml->xpath('/rss/channel/wp:wxr_version'); + if ( ! $wxr_version ) + return new WP_Error( 'WXR_parse_error', __( 'This does not appear to = be a WXR file, missing/invalid WXR version number', 'wordpress-importer' = ) ); + + $wxr_version =3D (string) trim( $wxr_version[0] ); + // confirm that we are dealing with the correct file format + if ( ! preg_match( '/^\d+\.\d+$/', $wxr_version ) ) + return new WP_Error( 'WXR_parse_error', __( 'This does not appear to = be a WXR file, missing/invalid WXR version number', 'wordpress-importer' = ) ); + + $base_url =3D $xml->xpath('/rss/channel/wp:base_site_url'); + $base_url =3D (string) trim( $base_url[0] ); + + $namespaces =3D $xml->getDocNamespaces(); + if ( ! isset( $namespaces['wp'] ) ) + $namespaces['wp'] =3D 'http://wordpress.org/export/1.1/'; + if ( ! isset( $namespaces['excerpt'] ) ) + $namespaces['excerpt'] =3D 'http://wordpress.org/export/1.1/excerpt/'= ; + + // grab authors + foreach ( $xml->xpath('/rss/channel/wp:author') as $author_arr ) { + $a =3D $author_arr->children( $namespaces['wp'] ); + $login =3D (string) $a->author_login; + $authors[$login] =3D array( + 'author_id' =3D> (int) $a->author_id, + 'author_login' =3D> $login, + 'author_email' =3D> (string) $a->author_email, + 'author_display_name' =3D> (string) $a->author_display_name, + 'author_first_name' =3D> (string) $a->author_first_name, + 'author_last_name' =3D> (string) $a->author_last_name + ); + } + + // grab cats, tags and terms + foreach ( $xml->xpath('/rss/channel/wp:category') as $term_arr ) { + $t =3D $term_arr->children( $namespaces['wp'] ); + $categories[] =3D array( + 'term_id' =3D> (int) $t->term_id, + 'category_nicename' =3D> (string) $t->category_nicename, + 'category_parent' =3D> (string) $t->category_parent, + 'cat_name' =3D> (string) $t->cat_name, + 'category_description' =3D> (string) $t->category_description + ); + } + + foreach ( $xml->xpath('/rss/channel/wp:tag') as $term_arr ) { + $t =3D $term_arr->children( $namespaces['wp'] ); + $tags[] =3D array( + 'term_id' =3D> (int) $t->term_id, + 'tag_slug' =3D> (string) $t->tag_slug, + 'tag_name' =3D> (string) $t->tag_name, + 'tag_description' =3D> (string) $t->tag_description + ); + } + + foreach ( $xml->xpath('/rss/channel/wp:term') as $term_arr ) { + $t =3D $term_arr->children( $namespaces['wp'] ); + $terms[] =3D array( + 'term_id' =3D> (int) $t->term_id, + 'term_taxonomy' =3D> (string) $t->term_taxonomy, + 'slug' =3D> (string) $t->term_slug, + 'term_parent' =3D> (string) $t->term_parent, + 'term_name' =3D> (string) $t->term_name, + 'term_description' =3D> (string) $t->term_description + ); + } + + // grab posts + foreach ( $xml->channel->item as $item ) { + $post =3D array( + 'post_title' =3D> (string) $item->title, + 'guid' =3D> (string) $item->guid, + ); + + $dc =3D $item->children( 'http://purl.org/dc/elements/1.1/' ); + $post['post_author'] =3D (string) $dc->creator; + + $content =3D $item->children( 'http://purl.org/rss/1.0/modules/conten= t/' ); + $excerpt =3D $item->children( $namespaces['excerpt'] ); + $post['post_content'] =3D (string) $content->encoded; + $post['post_excerpt'] =3D (string) $excerpt->encoded; + + $wp =3D $item->children( $namespaces['wp'] ); + $post['post_id'] =3D (int) $wp->post_id; + $post['post_date'] =3D (string) $wp->post_date; + $post['post_date_gmt'] =3D (string) $wp->post_date_gmt; + $post['comment_status'] =3D (string) $wp->comment_status; + $post['ping_status'] =3D (string) $wp->ping_status; + $post['post_name'] =3D (string) $wp->post_name; + $post['status'] =3D (string) $wp->status; + $post['post_parent'] =3D (int) $wp->post_parent; + $post['menu_order'] =3D (int) $wp->menu_order; + $post['post_type'] =3D (string) $wp->post_type; + $post['post_password'] =3D (string) $wp->post_password; + $post['is_sticky'] =3D (int) $wp->is_sticky; + + foreach ( $item->category as $c ) { + $att =3D $c->attributes(); + if ( isset( $att['nicename'] ) ) + $post['terms'][] =3D array( + 'name' =3D> (string) $c, + 'slug' =3D> (string) $att['nicename'], + 'domain' =3D> (string) $att['domain'] + ); + } + + foreach ( $wp->postmeta as $meta ) { + $post['postmeta'][] =3D array( + 'key' =3D> (string) $meta->meta_key, + 'value' =3D> (string) $meta->meta_value, + ); + } + + foreach ( $wp->comment as $comment ) { + $post['comments'][] =3D array( + 'comment_id' =3D> (int) $comment->comment_id, + 'comment_author' =3D> (string) $comment->comment_author, + 'comment_author_email' =3D> (string) $comment->comment_author_email= , + 'comment_author_IP' =3D> (string) $comment->comment_author_IP, + 'comment_author_url' =3D> (string) $comment->comment_author_url, + 'comment_date' =3D> (string) $comment->comment_date, + 'comment_date_gmt' =3D> (string) $comment->comment_date_gmt, + 'comment_content' =3D> (string) $comment->comment_content, + 'comment_approved' =3D> (string) $comment->comment_approved, + 'comment_type' =3D> (string) $comment->comment_type, + 'comment_parent' =3D> (string) $comment->comment_parent, + 'comment_user_id' =3D> (int) $comment->comment_user_id, + ); + } + + $posts[] =3D $post; + } + + return array( + 'authors' =3D> $authors, + 'posts' =3D> $posts, + 'categories' =3D> $categories, + 'tags' =3D> $tags, + 'terms' =3D> $terms, + 'base_url' =3D> $base_url, + 'version' =3D> $wxr_version + ); + } +} + +/** + * WXR Parser that makes use of the XML Parser PHP extension. + */ +class WXR_Parser_XML { + var $wp_tags =3D array( + 'wp:post_id', 'wp:post_date', 'wp:post_date_gmt', 'wp:comment_status',= 'wp:ping_status', + 'wp:status', 'wp:post_name', 'wp:post_parent', 'wp:menu_order', 'wp:po= st_type', 'wp:post_password', + 'wp:is_sticky', 'wp:term_id', 'wp:category_nicename', 'wp:category_par= ent', 'wp:cat_name', 'wp:category_description', + 'wp:tag_slug', 'wp:tag_name', 'wp:tag_description', 'wp:term_taxonomy'= , 'wp:term_parent', + 'wp:term_name', 'wp:term_description', 'wp:author_id', 'wp:author_logi= n', 'wp:author_email', 'wp:author_display_name', + 'wp:author_first_name', 'wp:author_last_name', + ); + var $wp_sub_tags =3D array( + 'wp:comment_id', 'wp:comment_author', 'wp:comment_author_email', 'wp:c= omment_author_url', + 'wp:comment_author_IP', 'wp:comment_date', 'wp:comment_date_gmt', 'wp:= comment_content', + 'wp:comment_approved', 'wp:comment_type', 'wp:comment_parent', 'wp:com= ment_user_id', + ); + + function parse( $file ) { + $this->wxr_version =3D $this->in_post =3D $this->cdata =3D $this->data= =3D $this->sub_data =3D $this->in_tag =3D $this->in_sub_tag =3D false; + $this->authors =3D $this->posts =3D $this->term =3D $this->category =3D= $this->tag =3D array(); + + $xml =3D xml_parser_create( 'UTF-8' ); + xml_parser_set_option( $xml, XML_OPTION_SKIP_WHITE, 1 ); + xml_parser_set_option( $xml, XML_OPTION_CASE_FOLDING, 0 ); + xml_set_object( $xml, $this ); + xml_set_character_data_handler( $xml, 'cdata' ); + xml_set_element_handler( $xml, 'tag_open', 'tag_close' ); + + if ( ! xml_parse( $xml, file_get_contents( $file ), true ) ) { + $current_line =3D xml_get_current_line_number( $xml ); + $current_column =3D xml_get_current_column_number( $xml ); + $error_code =3D xml_get_error_code( $xml ); + $error_string =3D xml_error_string( $error_code ); + return new WP_Error( 'XML_parse_error', 'There was an error when read= ing this WXR file', array( $current_line, $current_column, $error_string = ) ); + } + xml_parser_free( $xml ); + + if ( ! preg_match( '/^\d+\.\d+$/', $this->wxr_version ) ) + return new WP_Error( 'WXR_parse_error', __( 'This does not appear to = be a WXR file, missing/invalid WXR version number', 'wordpress-importer' = ) ); + + return array( + 'authors' =3D> $this->authors, + 'posts' =3D> $this->posts, + 'categories' =3D> $this->category, + 'tags' =3D> $this->tag, + 'terms' =3D> $this->term, + 'base_url' =3D> $this->base_url, + 'version' =3D> $this->wxr_version + ); + } + + function tag_open( $parse, $tag, $attr ) { + if ( in_array( $tag, $this->wp_tags ) ) { + $this->in_tag =3D substr( $tag, 3 ); + return; + } + + if ( in_array( $tag, $this->wp_sub_tags ) ) { + $this->in_sub_tag =3D substr( $tag, 3 ); + return; + } + + switch ( $tag ) { + case 'category': + if ( isset($attr['domain'], $attr['nicename']) ) { + $this->sub_data['domain'] =3D $attr['domain']; + $this->sub_data['slug'] =3D $attr['nicename']; + } + break; + case 'item': $this->in_post =3D true; + case 'title': if ( $this->in_post ) $this->in_tag =3D 'post_title'; b= reak; + case 'guid': $this->in_tag =3D 'guid'; break; + case 'dc:creator': $this->in_tag =3D 'post_author'; break; + case 'content:encoded': $this->in_tag =3D 'post_content'; break; + case 'excerpt:encoded': $this->in_tag =3D 'post_excerpt'; break; + + case 'wp:term_slug': $this->in_tag =3D 'slug'; break; + case 'wp:meta_key': $this->in_sub_tag =3D 'key'; break; + case 'wp:meta_value': $this->in_sub_tag =3D 'value'; break; + } + } + + function cdata( $parser, $cdata ) { + if ( ! trim( $cdata ) ) + return; + + $this->cdata .=3D trim( $cdata ); + } + + function tag_close( $parser, $tag ) { + switch ( $tag ) { + case 'wp:comment': + if ( ! empty( $this->sub_data ) ) + $this->data['comments'][] =3D $this->sub_data; + $this->sub_data =3D false; + break; + case 'category': + if ( ! empty( $this->sub_data ) ) { + $this->sub_data['name'] =3D $this->cdata; + $this->data['terms'][] =3D $this->sub_data; + } + $this->sub_data =3D false; + break; + case 'wp:postmeta': + if ( ! empty( $this->sub_data ) ) + $this->data['postmeta'][] =3D $this->sub_data; + $this->sub_data =3D false; + break; + case 'item': + $this->posts[] =3D $this->data; + $this->data =3D false; + break; + case 'wp:category': + case 'wp:tag': + case 'wp:term': + $n =3D substr( $tag, 3 ); + array_push( $this->$n, $this->data ); + $this->data =3D false; + break; + case 'wp:author': + if ( ! empty($this->data['author_login']) ) + $this->authors[$this->data['author_login']] =3D $this->data; + $this->data =3D false; + break; + case 'wp:base_site_url': + $this->base_url =3D $this->cdata; + break; + case 'wp:wxr_version': + $this->wxr_version =3D $this->cdata; + break; + + default: + if ( $this->in_sub_tag ) { + $this->sub_data[$this->in_sub_tag] =3D ! empty( $this->cdata ) ? $t= his->cdata : ''; + $this->in_sub_tag =3D false; + } else if ( $this->in_tag ) { + $this->data[$this->in_tag] =3D ! empty( $this->cdata ) ? $this->cda= ta : ''; + $this->in_tag =3D false; + } + } + + $this->cdata =3D false; + } +} + +/** + * WXR Parser that uses regular expressions. Fallback for installs witho= ut an XML parser. + */ +class WXR_Parser_Regex { + var $authors =3D array(); + var $posts =3D array(); + var $categories =3D array(); + var $tags =3D array(); + var $terms =3D array(); + var $base_url =3D ''; + + function WXR_Parser_Regex() { + $this->__construct(); + } + + function __construct() { + $this->has_gzip =3D is_callable( 'gzopen' ); + } + + function parse( $file ) { + $wxr_version =3D $in_post =3D false; + + $fp =3D $this->fopen( $file, 'r' ); + if ( $fp ) { + while ( ! $this->feof( $fp ) ) { + $importline =3D rtrim( $this->fgets( $fp ) ); + + if ( ! $wxr_version && preg_match( '|(\d+\.\d+)|', $importline, $version ) ) + $wxr_version =3D $version[1]; + + if ( false !=3D=3D strpos( $importline, '' ) ) { + preg_match( '|(.*?)|is', $impo= rtline, $url ); + $this->base_url =3D $url[1]; + continue; + } + if ( false !=3D=3D strpos( $importline, '' ) ) { + preg_match( '|(.*?)|is', $importline, $c= ategory ); + $this->categories[] =3D $this->process_category( $category[1] ); + continue; + } + if ( false !=3D=3D strpos( $importline, '' ) ) { + preg_match( '|(.*?)|is', $importline, $tag ); + $this->tags[] =3D $this->process_tag( $tag[1] ); + continue; + } + if ( false !=3D=3D strpos( $importline, '' ) ) { + preg_match( '|(.*?)|is', $importline, $term ); + $this->terms[] =3D $this->process_term( $term[1] ); + continue; + } + if ( false !=3D=3D strpos( $importline, '' ) ) { + preg_match( '|(.*?)|is', $importline, $autho= r ); + $a =3D $this->process_author( $author[1] ); + $this->authors[$a['author_login']] =3D $a; + continue; + } + if ( false !=3D=3D strpos( $importline, '' ) ) { + $post =3D ''; + $in_post =3D true; + continue; + } + if ( false !=3D=3D strpos( $importline, '' ) ) { + $in_post =3D false; + $this->posts[] =3D $this->process_post( $post ); + continue; + } + if ( $in_post ) { + $post .=3D $importline . "\n"; + } + } + + $this->fclose($fp); + } + + if ( ! $wxr_version ) + return new WP_Error( 'WXR_parse_error', __( 'This does not appear to = be a WXR file, missing/invalid WXR version number', 'wordpress-importer' = ) ); + + return array( + 'authors' =3D> $this->authors, + 'posts' =3D> $this->posts, + 'categories' =3D> $this->categories, + 'tags' =3D> $this->tags, + 'terms' =3D> $this->terms, + 'base_url' =3D> $this->base_url, + 'version' =3D> $wxr_version + ); + } + + function get_tag( $string, $tag ) { + global $wpdb; + preg_match( "|<$tag.*?>(.*?)|is", $string, $return ); + if ( isset( $return[1] ) ) { + $return =3D preg_replace( '|^$|s', '$1', $return[= 1] ); + $return =3D $wpdb->escape( trim( $return ) ); + } else { + $return =3D ''; + } + return $return; + } + + function process_category( $c ) { + return array( + 'term_id' =3D> $this->get_tag( $c, 'wp:term_id' ), + 'cat_name' =3D> $this->get_tag( $c, 'wp:cat_name' ), + 'category_nicename' =3D> $this->get_tag( $c, 'wp:category_nicename' )= , + 'category_parent' =3D> $this->get_tag( $c, 'wp:category_parent' ), + 'category_description' =3D> $this->get_tag( $c, 'wp:category_descript= ion' ), + ); + } + + function process_tag( $t ) { + return array( + 'term_id' =3D> $this->get_tag( $t, 'wp:term_id' ), + 'tag_name' =3D> $this->get_tag( $t, 'wp:tag_name' ), + 'tag_slug' =3D> $this->get_tag( $t, 'wp:tag_slug' ), + 'tag_description' =3D> $this->get_tag( $t, 'wp:tag_description' ), + ); + } + + function process_term( $t ) { + return array( + 'term_id' =3D> $this->get_tag( $t, 'wp:term_id' ), + 'term_taxonomy' =3D> $this->get_tag( $t, 'wp:term_taxonomy' ), + 'slug' =3D> $this->get_tag( $t, 'wp:term_slug' ), + 'term_parent' =3D> $this->get_tag( $t, 'wp:term_parent' ), + 'term_name' =3D> $this->get_tag( $t, 'wp:term_name' ), + 'term_description' =3D> $this->get_tag( $t, 'wp:term_description' ), + ); + } + + function process_author( $a ) { + return array( + 'author_id' =3D> $this->get_tag( $a, 'wp:author_id' ), + 'author_login' =3D> $this->get_tag( $a, 'wp:author_login' ), + 'author_email' =3D> $this->get_tag( $a, 'wp:author_email' ), + 'author_display_name' =3D> $this->get_tag( $a, 'wp:author_display_nam= e' ), + 'author_first_name' =3D> $this->get_tag( $a, 'wp:author_first_name' )= , + 'author_last_name' =3D> $this->get_tag( $a, 'wp:author_last_name' ), + ); + } + + function process_post( $post ) { + $post_id =3D $this->get_tag( $post, 'wp:post_id' ); + $post_title =3D $this->get_tag( $post, 'title' ); + $post_date =3D $this->get_tag( $post, 'wp:post_date' ); + $post_date_gmt =3D $this->get_tag( $post, 'wp:post_date_gmt' ); + $comment_status =3D $this->get_tag( $post, 'wp:comment_status' ); + $ping_status =3D $this->get_tag( $post, 'wp:ping_status' ); + $status =3D $this->get_tag( $post, 'wp:status' ); + $post_name =3D $this->get_tag( $post, 'wp:post_name' ); + $post_parent =3D $this->get_tag( $post, 'wp:post_parent' ); + $menu_order =3D $this->get_tag( $post, 'wp:menu_order' ); + $post_type =3D $this->get_tag( $post, 'wp:post_type' ); + $post_password =3D $this->get_tag( $post, 'wp:post_password' ); + $is_sticky =3D $this->get_tag( $post, 'wp:is_sticky' ); + $guid =3D $this->get_tag( $post, 'guid' ); + $post_author =3D $this->get_tag( $post, 'dc:creator' ); + + $post_excerpt =3D $this->get_tag( $post, 'excerpt:encoded' ); + $post_excerpt =3D preg_replace_callback( '|<(/?[A-Z]+)|', array( &$thi= s, '_normalize_tag' ), $post_excerpt ); + $post_excerpt =3D str_replace( '
', '
', $post_excerpt ); + $post_excerpt =3D str_replace( '
', '
', $post_excerpt ); + + $post_content =3D $this->get_tag( $post, 'content:encoded' ); + $post_content =3D preg_replace_callback( '|<(/?[A-Z]+)|', array( &$thi= s, '_normalize_tag' ), $post_content ); + $post_content =3D str_replace( '
', '
', $post_content ); + $post_content =3D str_replace( '
', '
', $post_content ); + + $postdata =3D compact( 'post_id', 'post_author', 'post_date', 'post_da= te_gmt', 'post_content', 'post_excerpt', + 'post_title', 'status', 'post_name', 'comment_status', 'ping_status',= 'guid', 'post_parent', + 'menu_order', 'post_type', 'post_password', 'is_sticky' + ); + + preg_match_all( '|= (.+?)|is', $post, $terms, PREG_SET_ORDER ); + foreach ( $terms as $t ) { + $post_terms[] =3D array( + 'slug' =3D> $t[2], + 'domain' =3D> $t[1], + 'name' =3D> str_replace( array( '' ), '', $t[3] ), + ); + } + if ( ! empty( $post_terms ) ) $postdata['terms'] =3D $post_terms; + + preg_match_all( '|(.+?)|is', $post, $comments= ); + $comments =3D $comments[1]; + if ( $comments ) { + foreach ( $comments as $comment ) { + $post_comments[] =3D array( + 'comment_id' =3D> $this->get_tag( $comment, 'wp:comment_id' ), + 'comment_author' =3D> $this->get_tag( $comment, 'wp:comment_author'= ), + 'comment_author_email' =3D> $this->get_tag( $comment, 'wp:comment_a= uthor_email' ), + 'comment_author_IP' =3D> $this->get_tag( $comment, 'wp:comment_auth= or_IP' ), + 'comment_author_url' =3D> $this->get_tag( $comment, 'wp:comment_aut= hor_url' ), + 'comment_date' =3D> $this->get_tag( $comment, 'wp:comment_date' ), + 'comment_date_gmt' =3D> $this->get_tag( $comment, 'wp:comment_date_= gmt' ), + 'comment_content' =3D> $this->get_tag( $comment, 'wp:comment_conten= t' ), + 'comment_approved' =3D> $this->get_tag( $comment, 'wp:comment_appro= ved' ), + 'comment_type' =3D> $this->get_tag( $comment, 'wp:comment_type' ), + 'comment_parent' =3D> $this->get_tag( $comment, 'wp:comment_parent'= ), + ); + } + } + if ( ! empty( $post_comments ) ) $postdata['comments'] =3D $post_comme= nts; + + preg_match_all( '|(.+?)|is', $post, $postme= ta ); + $postmeta =3D $postmeta[1]; + if ( $postmeta ) { + foreach ( $postmeta as $p ) { + $post_postmeta[] =3D array( + 'key' =3D> $this->get_tag( $p, 'wp:meta_key' ), + 'value' =3D> $this->get_tag( $p, 'wp:meta_value' ), + ); + } + } + if ( ! empty( $post_postmeta ) ) $postdata['postmeta'] =3D $post_postm= eta; + + return $postdata; + } + + function _normalize_tag( $matches ) { + return '<' . strtolower( $matches[1] ); + } + + function fopen( $filename, $mode =3D 'r' ) { + if ( $this->has_gzip ) + return gzopen( $filename, $mode ); + return fopen( $filename, $mode ); + } + + function feof( $fp ) { + if ( $this->has_gzip ) + return gzeof( $fp ); + return feof( $fp ); + } + + function fgets( $fp, $len =3D 8192 ) { + if ( $this->has_gzip ) + return gzgets( $fp, $len ); + return fgets( $fp, $len ); + } + + function fclose( $fp ) { + if ( $this->has_gzip ) + return gzclose( $fp ); + return fclose( $fp ); + } +} diff --git a/plugins/wordpress-importer/readme.txt b/plugins/wordpress-im= porter/readme.txt new file mode 100644 index 0000000..06689a0 --- /dev/null +++ b/plugins/wordpress-importer/readme.txt @@ -0,0 +1,41 @@ +=3D=3D=3D Plugin Name =3D=3D=3D +Contributors: wordpressdotorg +Donate link:=20 +Tags: importer, wordpress +Requires at least: 3.0 +Tested up to: 3.1 +Stable tag: 0.2 + +Import posts, pages, comments, custom fields, categories, tags and more = from a WordPress export file. + +=3D=3D Description =3D=3D + +Import posts, pages, comments, custom fields, categories, tags and more = from a WordPress export file. + +=3D=3D Installation =3D=3D + +1. Upload the `wordpress-importer` folder to the `/wp-content/plugins/` = directory +1. Activate the plugin through the 'Plugins' menu in WordPress +1. Go to the Tools -> Import screen, click on WordPress + +=3D=3D Changelog =3D=3D + +=3D 0.3 =3D +* Use an XML Parser if possible +* Proper import support for nav menus +* ... and more, see [Trac ticket #15197](http://core.trac.wordpress.org/= ticket/15197) + +=3D 0.1 =3D +* Initial release + +=3D=3D Upgrade Notice =3D=3D + +=3D 0.3 =3D +Upgrade for a more robust and reliable experience when importing WordPre= ss export files, and for compatibility with WordPress 3.1. + +=3D=3D Filters =3D=3D + +The importer has a couple of filters to allow you to completely enable/b= lock certain features: +* `import_allow_create_users`: return false if you only want to allow ma= pping to existing users +* `import_allow_fetch_attachments`: return false if you do not wish to a= llow importing and downloading of attachments +* `import_attachment_size_limit`: return an integer value for the maximu= m file size in bytes to save (default is 0, which is unlimited) diff --git a/plugins/wordpress-importer/wordpress-importer.php b/plugins/= wordpress-importer/wordpress-importer.php new file mode 100644 index 0000000..36285e2 --- /dev/null +++ b/plugins/wordpress-importer/wordpress-importer.php @@ -0,0 +1,1060 @@ +header(); + + $step =3D empty( $_GET['step'] ) ? 0 : (int) $_GET['step']; + switch ( $step ) { + case 0: + $this->greet(); + break; + case 1: + check_admin_referer( 'import-upload' ); + if ( $this->handle_upload() ) + $this->import_options(); + break; + case 2: + check_admin_referer( 'import-wordpress' ); + $this->fetch_attachments =3D ( ! empty( $_POST['fetch_attachments'] = ) && $this->allow_fetch_attachments() ); + $this->id =3D (int) $_POST['import_id']; + $file =3D get_attached_file( $this->id ); + set_time_limit(0); + $this->import( $file ); + break; + } + + $this->footer(); + } + + /** + * The main controller for the actual import stage. + * + * @param string $file Path to the WXR file for importing + */ + function import( $file ) { + add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' = ) ); + + $this->import_start( $file ); + + $this->get_author_mapping(); + + wp_suspend_cache_invalidation( true ); + $this->process_categories(); + $this->process_tags(); + $this->process_terms(); + $this->process_posts(); + wp_suspend_cache_invalidation( false ); + + // update incorrect/missing information in the DB + $this->backfill_parents(); + $this->backfill_attachment_urls(); + $this->remap_featured_images(); + + $this->import_end(); + } + + /** + * Parses the WXR file and prepares us for the task of processing parse= d data + * + * @param string $file Path to the WXR file for importing + */ + function import_start( $file ) { + if ( ! is_file($file) ) { + echo '

' . __( 'Sorry, there has been an error.', 'wordpres= s-importer' ) . '
'; + echo __( 'The file does not exist, please try again.', 'wordpress-imp= orter' ) . '

'; + $this->footer(); + die(); + } + + $import_data =3D $this->parse( $file ); + + if ( is_wp_error( $import_data ) ) { + echo '

' . __( 'Sorry, there has been an error.', 'wordpres= s-importer' ) . '
'; + echo esc_html( $import_data->get_error_message() ) . '

'; + $this->footer(); + die(); + } + + $this->version =3D $import_data['version']; + $this->get_authors_from_import( $import_data ); + $this->posts =3D $import_data['posts']; + $this->terms =3D $import_data['terms']; + $this->categories =3D $import_data['categories']; + $this->tags =3D $import_data['tags']; + $this->base_url =3D esc_url( $import_data['base_url'] ); + + wp_defer_term_counting( true ); + wp_defer_comment_counting( true ); + + do_action( 'import_start' ); + } + + /** + * Performs post-import cleanup of files and the cache + */ + function import_end() { + wp_import_cleanup( $this->id ); + + wp_cache_flush(); + foreach ( get_taxonomies() as $tax ) { + delete_option( "{$tax}_children" ); + _get_term_hierarchy( $tax ); + } + + wp_defer_term_counting( false ); + wp_defer_comment_counting( false ); + + echo '

' . __( 'All done.', 'wordpress-importer' ) . ' ' . __( 'Have fun!', 'wordpress-importer' ) . '' .= '

'; + echo '

' . __( 'Remember to update the passwords and roles of import= ed users.', 'wordpress-importer' ) . '

'; + + do_action( 'import_end' ); + } + + /** + * Handles the WXR upload and initial parsing of the file to prepare fo= r + * displaying author import options + * + * @return bool False if error uploading or invalid file, true otherwis= e + */ + function handle_upload() { + $file =3D wp_import_handle_upload(); + + if ( isset( $file['error'] ) ) { + echo '

' . __( 'Sorry, there has been an error.', 'wordpres= s-importer' ) . '
'; + echo esc_html( $file['error'] ) . '

'; + return false; + } + + $this->id =3D (int) $file['id']; + $import_data =3D $this->parse( $file['file'] ); + if ( is_wp_error( $import_data ) ) { + echo '

' . __( 'Sorry, there has been an error.', 'wordpres= s-importer' ) . '
'; + echo esc_html( $import_data->get_error_message() ) . '

'; + return false; + } + + $this->version =3D $import_data['version']; + if ( $this->version > $this->max_wxr_version ) { + echo '

'; + printf( __( 'This WXR file (version %s) may not be supported by this = version of the importer. Please consider updating.', 'wordpress-importer'= ), esc_html($import_data['version']) ); + echo '

'; + } + + $this->get_authors_from_import( $import_data ); + + return true; + } + + /** + * Retrieve authors from parsed WXR data + * + * Uses the provided author information from WXR 1.1 files + * or extracts info from each post for WXR 1.0 files + * + * @param array $import_data Data returned by a WXR parser + */ + function get_authors_from_import( $import_data ) { + if ( ! empty( $import_data['authors'] ) ) { + $this->authors =3D $import_data['authors']; + // no author information, grab it from the posts + } else { + foreach ( $import_data['posts'] as $post ) { + $login =3D sanitize_user( $post['post_author'], true ); + if ( empty( $login ) ) { + printf( __( 'Failed to import author %s. Their posts will be attrib= uted to the current user.', 'wordpress-importer' ), esc_html( $post['post= _author'] ) ); + echo '
'; + continue; + } + + if ( ! isset($this->authors[$login]) ) + $this->authors[$login] =3D array( + 'author_login' =3D> $login, + 'author_display_name' =3D> $post['post_author'] + ); + } + } + } + + /** + * Display pre-import options, author importing/mapping and option to + * fetch attachments + */ + function import_options() { + $j =3D 0; +?> +
+' . esc_html( $author['author_display_name'] ); + if ( $this->version !=3D '1.0' ) echo ' (' . esc_html( $author['author= _login'] ) . ')'; + echo '
" method=3D"post"> + + id= ; ?>" /> + +authors ) ) : ?> +

+

admins entries.', 'wordpress-importer' ); ?>

+allow_create_users() ) : ?> +

+ +

    +authors as $author ) : ?> +
  1. author_select( $j++, $author ); ?>
  2. + +
+ + +allow_fetch_attachments() ) : ?> +

+

+ + +

+ + +

" />

+

'; + + if ( $this->version !=3D '1.0' ) + echo '
'; + + $create_users =3D $this->allow_create_users(); + if ( $create_users ) { + if ( $this->version !=3D '1.0' ) { + _e( 'or create new user with login name:', 'wordpress-importer' ); + $value =3D ''; + } else { + _e( 'as a new user:', 'wordpress-importer' ); + $value =3D esc_attr( sanitize_user( $author['author_login'], true ) = ); + } + + echo '
'; + } + + if ( ! $create_users && $this->version =3D=3D '1.0' ) + _e( 'assign posts to an existing user:', 'wordpress-importer' ); + else + _e( 'or assign posts to an existing user:', 'wordpress-importer' ); + wp_dropdown_users( array( 'name' =3D> "user_map[$n]", 'multi' =3D> tru= e, 'show_option_all' =3D> __( '- Select -', 'wordpress-importer' ) ) ); + echo ''; + + if ( $this->version !=3D '1.0' ) + echo '
'; + } + + /** + * Map old author logins to local user IDs based on decisions made + * in import options form. Can map to an existing user, create a new us= er + * or falls back to the current user in case of error with either of th= e previous + */ + function get_author_mapping() { + if ( ! isset( $_POST['imported_authors'] ) ) + return; + + $create_users =3D $this->allow_create_users(); + + foreach ( (array) $_POST['imported_authors'] as $i =3D> $old_login ) { + $old_id =3D isset( $this->authors[$old_login]['author_id'] ) ? intval= ($this->authors[$old_login]['author_id']) : false; + + if ( ! empty( $_POST['user_map'][$i] ) ) { + $user =3D get_userdata( intval($_POST['user_map'][$i]) ); + if ( isset( $user->ID ) ) { + if ( $old_id ) + $this->processed_authors[$old_id] =3D $user->ID; + $this->author_mapping[$old_login] =3D $user->ID; + } + } else if ( $create_users ) { + if ( ! empty($_POST['user_new'][$i]) ) { + $user_id =3D wp_create_user( $_POST['user_new'][$i], wp_generate_pa= ssword() ); + } else if ( $this->version !=3D '1.0' ) { + $user_data =3D array( + 'user_login' =3D> $old_login, + 'user_pass' =3D> wp_generate_password(), + 'user_email' =3D> isset( $this->authors[$old_login]['author_email'= ] ) ? $this->authors[$old_login]['author_email'] : '', + 'display_name' =3D> $this->authors[$old_login]['author_display_nam= e'], + 'first_name' =3D> isset( $this->authors[$old_login]['author_first_= name'] ) ? $this->authors[$old_login]['author_first_name'] : '', + 'last_name' =3D> isset( $this->authors[$old_login]['author_last_na= me'] ) ? $this->authors[$old_login]['author_last_name'] : '', + ); + $user_id =3D wp_insert_user( $user_data ); + } + + if ( ! is_wp_error( $user_id ) ) { + if ( $old_id ) + $this->processed_authors[$old_id] =3D $user_id; + $this->author_mapping[$old_login] =3D $user_id; + } else { + printf( __( 'Failed to create new user for %s. Their posts will be = attributed to the current user.', 'wordpress-importer' ), esc_html($this-= >authors[$old_login]['author_display_name']) ); + if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) + echo ' ' . $user_id->get_error_message(); + echo '
'; + } + } + + // failsafe: if the user_id was invalid, default to the current user + if ( ! isset( $this->author_mapping[$old_login] ) ) { + if ( $old_id ) + $this->processed_authors[$old_id] =3D (int) get_current_user_id(); + $this->author_mapping[$old_login] =3D (int) get_current_user_id(); + } + } + } + + /** + * Create new categories based on import information + * + * Doesn't create a new category if its slug already exists + */ + function process_categories() { + if ( empty( $this->categories ) ) + return; + + foreach ( $this->categories as $cat ) { + // if the category already exists leave it alone + $term_id =3D term_exists( $cat['category_nicename'], 'category' ); + if ( $term_id ) { + if ( is_array($term_id) ) $term_id =3D $term_id['term_id']; + $this->processed_terms[intval($cat['term_id'])] =3D (int) $term_id; + continue; + } + + $category_parent =3D empty( $cat['category_parent'] ) ? 0 : category_= exists( $cat['category_parent'] ); + $category_description =3D isset( $cat['category_description'] ) ? $ca= t['category_description'] : ''; + $catarr =3D array( + 'category_nicename' =3D> $cat['category_nicename'], + 'category_parent' =3D> $category_parent, + 'cat_name' =3D> $cat['cat_name'], + 'category_description' =3D> $category_description + ); + + $id =3D wp_insert_category( $catarr ); + if ( ! is_wp_error( $id ) ) { + $this->processed_terms[intval($cat['term_id'])] =3D $id; + } else { + printf( __( 'Failed to import category %s', 'wordpress-importer' ), = esc_html($cat['category_nicename']) ); + if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) + echo ': ' . $id->get_error_message(); + echo '
'; + continue; + } + } + + unset( $this->categories ); + } + + /** + * Create new post tags based on import information + * + * Doesn't create a tag if its slug already exists + */ + function process_tags() { + if ( empty( $this->tags ) ) + return; + + foreach ( $this->tags as $tag ) { + // if the tag already exists leave it alone + $term_id =3D term_exists( $tag['tag_slug'], 'post_tag' ); + if ( $term_id ) { + if ( is_array($term_id) ) $term_id =3D $term_id['term_id']; + $this->processed_terms[intval($tag['term_id'])] =3D (int) $term_id; + continue; + } + + $tag_desc =3D isset( $tag['tag_description'] ) ? $tag['tag_descriptio= n'] : ''; + $tagarr =3D array( 'slug' =3D> $tag['tag_slug'], 'description' =3D> $= tag_desc ); + + $id =3D wp_insert_term( $tag['tag_name'], 'post_tag', $tagarr ); + if ( ! is_wp_error( $id ) ) { + $this->processed_terms[intval($tag['term_id'])] =3D $id['term_id']; + } else { + printf( __( 'Failed to import post tag %s', 'wordpress-importer' ), = esc_html($tag['tag_name']) ); + if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) + echo ': ' . $id->get_error_message(); + echo '
'; + continue; + } + } + + unset( $this->tags ); + } + + /** + * Create new terms based on import information + * + * Doesn't create a term its slug already exists + */ + function process_terms() { + if ( empty( $this->terms ) ) + return; + + foreach ( $this->terms as $term ) { + // if the term already exists in the correct taxonomy leave it alone + $term_id =3D term_exists( $term['slug'], $term['term_taxonomy'] ); + if ( $term_id ) { + if ( is_array($term_id) ) $term_id =3D $term_id['term_id']; + $this->processed_terms[intval($term['term_id'])] =3D (int) $term_id; + continue; + } + + if ( empty( $term['term_parent'] ) ) { + $parent =3D 0; + } else { + $parent =3D term_exists( $term['term_parent'], $term['term_taxonomy'= ] ); + if ( is_array( $parent ) ) $parent =3D $parent['term_id']; + } + $description =3D isset( $term['term_description'] ) ? $term['term_des= cription'] : ''; + $termarr =3D array( 'slug' =3D> $term['slug'], 'description' =3D> $de= scription, 'parent' =3D> intval($parent) ); + + $id =3D wp_insert_term( $term['term_name'], $term['term_taxonomy'], $= termarr ); + if ( ! is_wp_error( $id ) ) { + $this->processed_terms[intval($term['term_id'])] =3D $id['term_id']; + } else { + printf( __( 'Failed to import %s %s', 'wordpress-importer' ), esc_ht= ml($term['term_taxonomy']), esc_html($term['term_name']) ); + if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) + echo ': ' . $id->get_error_message(); + echo '
'; + continue; + } + } + + unset( $this->terms ); + } + + /** + * Create new posts based on import information + * + * Posts marked as having a parent which doesn't exist will become top = level items. + * Doesn't create a new post if: the post type doesn't exist, the given= post ID + * is already noted as imported or a post with the same title and date = already exists. + * Note that new/updated terms, comments and meta are imported for the = last of the above. + */ + function process_posts() { + foreach ( $this->posts as $post ) { + if ( ! post_type_exists( $post['post_type'] ) ) { + printf( __( 'Failed to import “%s”: Invalid post type %s= ', 'wordpress-importer' ), + esc_html($post['post_title']), esc_html($post['post_type']) ); + echo '
'; + continue; + } + + if ( isset( $this->processed_posts[$post['post_id']] ) ) + continue; + + if ( $post['status'] =3D=3D 'auto-draft' ) + continue; + + if ( 'nav_menu_item' =3D=3D $post['post_type'] ) { + $this->process_menu_item( $post ); + continue; + } + + $post_type_object =3D get_post_type_object( $post['post_type'] ); + + $post_exists =3D post_exists( $post['post_title'], '', $post['post_da= te'] ); + if ( $post_exists ) { + printf( __('%s “%s” already exists.', 'wordpress-importe= r'), $post_type_object->labels->singular_name, esc_html($post['post_title= ']) ); + echo '
'; + $comment_post_ID =3D $post_id =3D $post_exists; + } else { + $post_parent =3D (int) $post['post_parent']; + if ( $post_parent ) { + // if we already know the parent, map it to the new local ID + if ( isset( $this->processed_posts[$post_parent] ) ) { + $post_parent =3D $this->processed_posts[$post_parent]; + // otherwise record the parent for later + } else { + $this->post_orphans[intval($post['post_id'])] =3D $post_parent; + $post_parent =3D 0; + } + } + + // map the post author + $author =3D sanitize_user( $post['post_author'], true ); + if ( isset( $this->author_mapping[$author] ) ) + $author =3D $this->author_mapping[$author]; + else + $author =3D (int) get_current_user_id(); + + $postdata =3D array( + 'import_id' =3D> $post['post_id'], 'post_author' =3D> $author, 'pos= t_date' =3D> $post['post_date'], + 'post_date_gmt' =3D> $post['post_date_gmt'], 'post_content' =3D> $p= ost['post_content'], + 'post_excerpt' =3D> $post['post_excerpt'], 'post_title' =3D> $post[= 'post_title'], + 'post_status' =3D> $post['status'], 'post_name' =3D> $post['post_na= me'], + 'comment_status' =3D> $post['comment_status'], 'ping_status' =3D> $= post['ping_status'], + 'guid' =3D> $post['guid'], 'post_parent' =3D> $post_parent, 'menu_o= rder' =3D> $post['menu_order'], + 'post_type' =3D> $post['post_type'], 'post_password' =3D> $post['po= st_password'] + ); + + if ( 'attachment' =3D=3D $postdata['post_type'] ) { + $remote_url =3D ! empty($post['attachment_url']) ? $post['attachmen= t_url'] : $post['guid']; + $comment_post_ID =3D $post_id =3D $this->process_attachment( $postd= ata, $remote_url ); + } else { + $comment_post_ID =3D $post_id =3D wp_insert_post( $postdata, true )= ; + } + + if ( is_wp_error( $post_id ) ) { + printf( __( 'Failed to import %s “%s”', 'wordpress-impo= rter' ), + $post_type_object->labels->singular_name, esc_html($post['post_tit= le']) ); + if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) + echo ': ' . $post_id->get_error_message(); + echo '
'; + continue; + } + + if ( $post['is_sticky'] =3D=3D 1 ) + stick_post( $post_id ); + } + + // map pre-import ID to local ID + $this->processed_posts[intval($post['post_id'])] =3D (int) $post_id; + + // add categories, tags and other terms + if ( ! empty( $post['terms'] ) ) { + $terms_to_set =3D array(); + foreach ( $post['terms'] as $term ) { + // back compat with WXR 1.0 map 'tag' to 'post_tag' + $taxonomy =3D ( 'tag' =3D=3D $term['domain'] ) ? 'post_tag' : $term= ['domain']; + $term_exists =3D term_exists( $term['slug'], $taxonomy ); + $term_id =3D is_array( $term_exists ) ? $term_exists['term_id'] : $= term_exists; + if ( ! $term_id ) { + $t =3D wp_insert_term( $term['name'], $taxonomy, array( 'slug' =3D= > $term['slug'] ) ); + if ( ! is_wp_error( $t ) ) { + $term_id =3D $t['term_id']; + } else { + printf( __( 'Failed to import %s %s', 'wordpress-importer' ), esc= _html($taxonomy), esc_html($term['name']) ); + if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) + echo ': ' . $t->get_error_message(); + echo '
'; + continue; + } + } + $terms_to_set[$taxonomy][] =3D intval( $term_id ); + } + + foreach ( $terms_to_set as $tax =3D> $ids ) { + $tt_ids =3D wp_set_post_terms( $post_id, $ids, $tax ); + } + unset( $post['terms'], $terms_to_set ); + } + + // add/update comments + if ( ! empty( $post['comments'] ) ) { + $num_comments =3D 0; + $inserted_comments =3D array(); + foreach ( $post['comments'] as $comment ) { + $comment_id =3D $comment['comment_id']; + $newcomments[$comment_id]['comment_post_ID'] =3D $comment_post= _ID; + $newcomments[$comment_id]['comment_author'] =3D $comment['com= ment_author']; + $newcomments[$comment_id]['comment_author_email'] =3D $comment['com= ment_author_email']; + $newcomments[$comment_id]['comment_author_IP'] =3D $comment['com= ment_author_IP']; + $newcomments[$comment_id]['comment_author_url'] =3D $comment['com= ment_author_url']; + $newcomments[$comment_id]['comment_date'] =3D $comment['com= ment_date']; + $newcomments[$comment_id]['comment_date_gmt'] =3D $comment['com= ment_date_gmt']; + $newcomments[$comment_id]['comment_content'] =3D $comment['com= ment_content']; + $newcomments[$comment_id]['comment_approved'] =3D $comment['com= ment_approved']; + $newcomments[$comment_id]['comment_type'] =3D $comment['com= ment_type']; + $newcomments[$comment_id]['comment_parent'] =3D $comment['commen= t_parent']; + } + ksort( $newcomments ); + + foreach ( $newcomments as $key =3D> $comment ) { + // if this is a new post we can skip the comment_exists() check + if ( ! $post_exists || ! comment_exists( $comment['comment_author']= , $comment['comment_date'] ) ) { + if ( isset( $inserted_comments[$comment['comment_parent']] ) ) + $comment['comment_parent'] =3D $inserted_comments[$comment['comme= nt_parent']]; + $comment =3D wp_filter_comment( $comment ); + $inserted_comments[$key] =3D wp_insert_comment( $comment ); + $num_comments++; + } + } + unset( $newcomments, $inserted_comments, $post['comments'] ); + } + + // add/update post meta + if ( isset( $post['postmeta'] ) ) { + foreach ( $post['postmeta'] as $meta ) { + $key =3D apply_filters( 'import_post_meta_key', $meta['key'] ); + $value =3D false; + + if ( '_edit_last' =3D=3D $key ) { + if ( isset( $this->processed_authors[intval($meta['value'])] ) ) + $value =3D $this->processed_authors[intval($meta['value'])]; + else + $key =3D false; + } + + if ( $key ) { + // export gets meta straight from the DB so could have a serialize= d string + if ( ! $value ) + $value =3D maybe_unserialize( $meta['value'] ); + + update_post_meta( $post_id, $key, $value ); + do_action( 'import_post_meta', $post_id, $key, $value ); + + // if the post has a featured image, take note of this in case of = remap + if ( '_thumbnail_id' =3D=3D $key ) + $this->featured_images[$post_id] =3D (int) $value; + } + } + } + } + + unset( $this->posts ); + } + + /** + * Attempt to create a new menu item from import data + * + * Fails for draft, orphaned menu items and those without an associated= nav_menu + * or an invalid nav_menu term. If the post type or term object which t= he menu item + * represents doesn't exist then the menu item will not be imported (wa= its until the + * end of the import to retry again before discarding). + * + * @param array $item Menu item details from WXR file + */ + function process_menu_item( $item ) { + // skip draft, orphaned menu items + if ( 'draft' =3D=3D $item['status'] ) + return; + + $menu_slug =3D false; + if ( isset($item['terms']) ) { + // loop through terms, assume first nav_menu term is correct menu + foreach ( $item['terms'] as $term ) { + if ( 'nav_menu' =3D=3D $term['domain'] ) { + $menu_slug =3D $term['slug']; + break; + } + } + } + + // no nav_menu term associated with this menu item + if ( ! $menu_slug ) { + _e( 'Menu item skipped due to missing menu slug', 'wordpress-importer= ' ); + echo '
'; + return; + } + + $menu_id =3D term_exists( $menu_slug, 'nav_menu' ); + if ( ! $menu_id ) { + printf( __( 'Menu item skipped due to invalid menu slug: %s', 'wordpr= ess-importer' ), esc_html( $menu_slug ) ); + echo '
'; + return; + } else { + $menu_id =3D is_array( $menu_id ) ? $menu_id['term_id'] : $menu_id; + } + + foreach ( $item['postmeta'] as $meta ) + $$meta['key'] =3D $meta['value']; + + if ( 'taxonomy' =3D=3D $_menu_item_type && isset( $this->processed_ter= ms[intval($_menu_item_object_id)] ) ) { + $_menu_item_object_id =3D $this->processed_terms[intval($_menu_item_o= bject_id)]; + } else if ( 'post_type' =3D=3D $_menu_item_type && isset( $this->proce= ssed_posts[intval($_menu_item_object_id)] ) ) { + $_menu_item_object_id =3D $this->processed_posts[intval($_menu_item_o= bject_id)]; + } else if ( 'custom' !=3D $_menu_item_type ) { + // associated object is missing or not imported yet, we'll retry late= r + $this->missing_menu_items[] =3D $item; + return; + } + + if ( isset( $this->processed_menu_items[intval($_menu_item_menu_item_p= arent)] ) ) { + $_menu_item_menu_item_parent =3D $this->processed_menu_items[intval($= _menu_item_menu_item_parent)]; + } else if ( $_menu_item_menu_item_parent ) { + $this->menu_item_orphans[intval($item['post_id'])] =3D (int) $_menu_i= tem_menu_item_parent; + $_menu_item_menu_item_parent =3D 0; + } + + // wp_update_nav_menu_item expects CSS classes as a space separated st= ring + $_menu_item_classes =3D maybe_unserialize( $_menu_item_classes ); + if ( is_array( $_menu_item_classes ) ) + $_menu_item_classes =3D implode( ' ', $_menu_item_classes ); + + $args =3D array( + 'menu-item-object-id' =3D> $_menu_item_object_id, + 'menu-item-object' =3D> $_menu_item_object, + 'menu-item-parent-id' =3D> $_menu_item_menu_item_parent, + 'menu-item-position' =3D> intval( $item['menu_order'] ), + 'menu-item-type' =3D> $_menu_item_type, + 'menu-item-title' =3D> $item['post_title'], + 'menu-item-url' =3D> $_menu_item_url, + 'menu-item-description' =3D> $item['post_content'], + 'menu-item-attr-title' =3D> $item['post_excerpt'], + 'menu-item-target' =3D> $_menu_item_target, + 'menu-item-classes' =3D> $_menu_item_classes, + 'menu-item-xfn' =3D> $_menu_item_xfn, + 'menu-item-status' =3D> $item['status'] + ); + + $id =3D wp_update_nav_menu_item( $menu_id, 0, $args ); + if ( $id && ! is_wp_error( $id ) ) + $this->processed_menu_items[intval($item['post_id'])] =3D (int) $id; + } + + /** + * If fetching attachments is enabled then attempt to create a new atta= chment + * + * @param array $post Attachment post details from WXR + * @param string $url URL to fetch attachment from + * @return int|WP_Error Post ID on success, WP_Error otherwise + */ + function process_attachment( $post, $url ) { + if ( ! $this->fetch_attachments ) + return new WP_Error( 'attachment_processing_error', + __( 'Fetching attachments is not enabled', 'wordpress-importer' ) ); + + // if the URL is absolute, but does not contain address, then upload i= t assuming base_site_url + if ( preg_match( '|^/[\w\W]+$|', $url ) ) + $url =3D rtrim( $this->base_url, '/' ) . $url; + + $upload =3D $this->fetch_remote_file( $url, $post ); + if ( is_wp_error( $upload ) ) + return $upload; + + if ( $info =3D wp_check_filetype( $upload['file'] ) ) + $post['post_mime_type'] =3D $info['type']; + else + return new WP_Error( 'attachment_processing_error', __('Invalid file = type', 'wordpress-importer') ); + + $post['guid'] =3D $upload['url']; + + // as per wp-admin/includes/upload.php + $post_id =3D wp_insert_attachment( $post, $upload['file'] ); + wp_update_attachment_metadata( $post_id, wp_generate_attachment_metada= ta( $post_id, $upload['file'] ) ); + + // remap the thumbnail url. this isn't perfect because we're just gue= ssing the original url. + if ( preg_match( '@^image/@', $info['type'] ) && $thumb_url =3D wp_get= _attachment_thumb_url( $post_id ) ) { + $parts =3D pathinfo( $url ); + $ext =3D $parts['extension']; + $name =3D basename($parts['basename'], ".{$ext}"); + $this->url_remap[$parts['dirname'] . '/' . $name . '.thumbnail.' . $e= xt] =3D $thumb_url; + } + + return $post_id; + } + + /** + * Attempt to download a remote file attachment + * + * @param string $url URL of item to fetch + * @param array $post Attachment details + * @return array|WP_Error Local file location details on success, WP_Er= ror otherwise + */ + function fetch_remote_file( $url, $post ) { + add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeo= ut' ) ); + + // extract the file name and extension from the url + $file_name =3D basename( $url ); + + // get placeholder file in the upload dir with a unique, sanitized fil= ename + $upload =3D wp_upload_bits( $file_name, 0, '', $post['post_date'] ); + if ( $upload['error'] ) + return new WP_Error( 'upload_dir_error', $upload['error'] ); + + // fetch the remote url and write it to the placeholder file + $headers =3D wp_get_http( $url, $upload['file'] ); + + // request failed + if ( ! $headers ) { + @unlink( $upload['file'] ); + return new WP_Error( 'import_file_error', __('Remote server did not r= espond', 'wordpress-importer') ); + } + + // make sure the fetch was successful + if ( $headers['response'] !=3D '200' ) { + @unlink( $upload['file'] ); + return new WP_Error( 'import_file_error', sprintf( __('Remote server = returned error response %1$d %2$s', 'wordpress-importer'), esc_html($head= ers['response']), get_status_header_desc($headers['response']) ) ); + } + + $filesize =3D filesize( $upload['file'] ); + + if ( isset( $headers['content-length'] ) && $filesize !=3D $headers['c= ontent-length'] ) { + @unlink( $upload['file'] ); + return new WP_Error( 'import_file_error', __('Remote file is incorrec= t size', 'wordpress-importer') ); + } + + if ( 0 =3D=3D $filesize ) { + @unlink( $upload['file'] ); + return new WP_Error( 'import_file_error', __('Zero size file download= ed', 'wordpress-importer') ); + } + + $max_size =3D (int) $this->max_attachment_size(); + if ( ! empty( $max_size ) && $filesize > $max_size ) { + @unlink( $upload['file'] ); + return new WP_Error( 'import_file_error', sprintf(__('Remote file is = too large, limit is %s', 'wordpress-importer'), size_format($max_size) ) = ); + } + + // keep track of the old and new urls so we can substitute them later + $this->url_remap[$url] =3D $upload['url']; + $this->url_remap[$post['guid']] =3D $upload['url']; + // if the remote url is redirected somewhere else, keep track of the d= estination too + if ( isset($headers['x-final-location']) && $headers['x-final-location= '] !=3D $url ) + $this->url_remap[$headers['x-final-location']] =3D $upload['url']; + + return $upload; + } + + /** + * Attempt to associate posts and menu items with previously missing pa= rents + * + * An imported post's parent may not have been imported when it was fir= st created + * so try again. Similarly for child menu items and menu items which we= re missing + * the object (e.g. post) they represent in the menu + */ + function backfill_parents() { + global $wpdb; + + // find parents for post orphans + foreach ( $this->post_orphans as $child_id =3D> $parent_id ) { + $local_child_id =3D $local_parent_id =3D false; + if ( isset( $this->processed_posts[$child_id] ) ) + $local_child_id =3D $this->processed_posts[$child_id]; + if ( isset( $this->processed_posts[$parent_id] ) ) + $local_parent_id =3D $this->processed_posts[$parent_id]; + + if ( $local_child_id && $local_parent_id ) + $wpdb->update( $wpdb->posts, array( 'post_parent' =3D> $local_parent= _id ), array( 'ID' =3D> $local_child_id ), '%d', '%d' ); + } + + // all other posts/terms are imported, retry menu items with missing a= ssociated object + $missing_menu_items =3D $this->missing_menu_items; + foreach ( $missing_menu_items as $item ) + $this->process_menu_item( $item ); + + // find parents for menu item orphans + foreach ( $this->menu_item_orphans as $child_id =3D> $parent_id ) { + $local_child_id =3D $local_parent_id =3D 0; + if ( isset( $this->processed_menu_items[$child_id] ) ) + $local_child_id =3D $this->processed_menu_items[$child_id]; + if ( isset( $this->processed_menu_items[$parent_id] ) ) + $local_parent_id =3D $this->processed_menu_items[$parent_id]; + + if ( $local_child_id && $local_parent_id ) + update_post_meta( $local_child_id, '_menu_item_menu_item_parent', (i= nt) $local_parent_id ); + } + } + + /** + * Use stored mapping information to update old attachment URLs + */ + function backfill_attachment_urls() { + global $wpdb; + // make sure we do the longest urls first, in case one is a substring = of another + uksort( $this->url_remap, array(&$this, 'cmpr_strlen') ); + + foreach ( $this->url_remap as $from_url =3D> $to_url ) { + // remap urls in post_content + $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->posts} SET post_content = =3D REPLACE(post_content, %s, %s)", $from_url, $to_url) ); + // remap enclosure urls + $result =3D $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->postmeta} SE= T meta_value =3D REPLACE(meta_value, %s, %s) WHERE meta_key=3D'enclosure'= ", $from_url, $to_url) ); + } + } + + /** + * Update _thumbnail_id meta to new, imported attachment IDs + */ + function remap_featured_images() { + // cycle through posts that have a featured image + foreach ( $this->featured_images as $post_id =3D> $value ) { + if ( isset( $this->processed_posts[$value] ) ) { + $new_id =3D $this->processed_posts[$value]; + // only update if there's a difference + if ( $new_id !=3D $value ) + update_post_meta( $post_id, '_thumbnail_id', $new_id ); + } + } + } + + /** + * Parse a WXR file + * + * @param string $file Path to WXR file for parsing + * @return array Information gathered from the WXR file + */ + function parse( $file ) { + $parser =3D new WXR_Parser(); + return $parser->parse( $file ); + } + + // Display import page title + function header() { + echo '
'; + screen_icon(); + echo '

' . __( 'Import WordPress', 'wordpress-importer' ) . '

'= ; + + $updates =3D get_plugin_updates(); + $basename =3D plugin_basename(__FILE__); + if ( isset( $updates[$basename] ) ) { + $update =3D $updates[$basename]; + echo '

'; + printf( __( 'A new version of this importer is available. Please upda= te to version %s to ensure compatibility with newer export files.', 'word= press-importer' ), $update->update->new_version ); + echo '

'; + } + } + + // Close div.wrap + function footer() { + echo '
'; + } + + /** + * Display introductory text and file upload form + */ + function greet() { + echo '
'; + echo '

'.__( 'Howdy! Upload your WordPress eXtended RSS (WXR) file a= nd we’ll import the posts, pages, comments, custom fields, categori= es, and tags into this site.', 'wordpress-importer' ).'

'; + echo '

'.__( 'Choose a WXR file to upload, then click Upload file an= d import.', 'wordpress-importer' ).'

'; + wp_import_upload_form( 'admin.php?import=3Dwordpress&step=3D1' ); + echo '
'; + } + + /** + * Decide if the given meta key maps to information we will want to imp= ort + * + * @param string $key The meta key to check + * @return string|bool The key if we do want to import, false if not + */ + function is_valid_meta_key( $key ) { + // skip attachment metadata since we'll regenerate it from scratch + // skip _edit_lock as not relevant for import + if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metad= ata', '_edit_lock' ) ) ) + return false; + return $key; + } + + /** + * Decide whether or not the importer is allowed to create users. + * Default is true, can be filtered via import_allow_create_users + * + * @return bool True if creating users is allowed + */ + function allow_create_users() { + return apply_filters( 'import_allow_create_users', true ); + } + + /** + * Decide whether or not the importer should attempt to download attach= ment files. + * Default is true, can be filtered via import_allow_fetch_attachments.= The choice + * made at the import options screen must also be true, false here hide= s that checkbox. + * + * @return bool True if downloading attachments is allowed + */ + function allow_fetch_attachments() { + return apply_filters( 'import_allow_fetch_attachments', true ); + } + + /** + * Decide what the maximum file size for downloaded attachments is. + * Default is 0 (unlimited), can be filtered via import_attachment_size= _limit + * + * @return int Maximum attachment file size to import + */ + function max_attachment_size() { + return apply_filters( 'import_attachment_size_limit', 0 ); + } + + /** + * Added to http_request_timeout filter to force timeout at 60 seconds = during import + * @return int 60 + */ + function bump_request_timeout() { + return 60; + } + + // return the difference in length between two strings + function cmpr_strlen( $a, $b ) { + return strlen($b) - strlen($a); + } +} + +} // class_exists( 'WP_Importer' ) + +function wordpress_importer_init() { + load_plugin_textdomain( 'wordpress-importer', false, dirname( plugin_ba= sename( __FILE__ ) ) . '/languages' ); + + /** + * WordPress Importer object for registering the import callback + * @global WP_Import $wp_import + */ + $GLOBALS['wp_import'] =3D new WP_Import(); + register_importer( 'wordpress', 'WordPress', __('Import posts, = pages, comments, custom fields, categories, and tags from a Word= Press export file.', 'wordpress-importer'), array( $GLOBALS['wp_import'],= 'dispatch' ) ); +} +add_action( 'admin_init', 'wordpress_importer_init' );