从Functions.php中导入WordPress XML文件

时间:2013-04-17 作者:Dwayne Charrington

我正在开发一个主题,它有不同的添加内容的方法,因此,Wordpress的默认安装不会显示任何内容。我想知道在主题被激活后,是否可以通过内部函数和/或挂钩自动导入XML文件?

User installs theme > User activates theme > Code behind the scenes loads up an XML file and performs a silent import of its contents

当前要导入XML文件,您必须为Wordpress安装Wordpress导入器插件,然后手动导入该文件,选择一个用户将导入的内容与关联,并决定是否要导入媒体附件。我发现这一步对于我所针对的客户类型来说太令人困惑了,我想有效地消除对这一步的需求。

我对Wordpress导入器脚本做了一些深入的研究,有很多函数调用,我需要做什么才能去掉需要用户输入的部分,并使用类及其方法目录导入文件?我真的不知道从哪里开始。

我的客户是商人,所以即使是像导入XML文件这样简单的事情也会让他们感到困惑,而且他们没有时间这样做,因此存在出错的空间,特别是如果他们尝试多次导入导致重复页面的话。

提前谢谢你。

Edit/Clarification

这里似乎有很多困惑。我不是问如何检查主题是否已激活,我已经对该部分进行了排序。我想知道如何解析XML导入文件并自动导入,而无需用户的努力。我本质上是想自动化Wordpress导入插件,您可以使用该插件手动导入XML文件,选择作者,选择下载并在我的函数中导入附件。php。

而不是需要一个插件或要求我的客户缺乏计算机知识,也不想学习如何使用插件。

4 个回复
最合适的回答,由SO网友:Ralf912 整理而成

Your question is a bit specific if you "only" want to automatically import some posts/pages. There are other ways to do this then using a XML export file.

If you have text-only posts, then you should use LOAD DATA INFILE. At first you have to export your posts.

global $wpdb, $wp_filesystem;

$tables = array(
        \'posts\'    => array( \'posts\', \'postmeta\' ),
        \'comments\' => array( \'comments\', \'commentmeta\' ),
        \'terms\'    => array( \'terms\', \'term_taxonomy\', \'term_relationships\' ),
        \'users\'    => array( \'user\', \'usermeta\' ),
        \'links\'    => array( \'links\' ),
        \'options\'  => array( \'options\' ),
        \'other\'    => array(),
        // for multiside
        \'multiside\' => array( \'blogs\', \'signups\', \'site\', \'sitemeta\', \'sitecategories\', \'registration_log\', \'blog_versions\' )

);

$exports = array( \'posts\', \'comments\', \'users\' );

$exportdir = TEMPLATEPATH . \'/export\';

if ( ! is_dir( $exportdir ) ) {
    $mkdir = wp_mkdir_p( $exportdir );
    if ( false == $mkdir || ! is_dir( $exportdir ) )
        throw new Exception( \'Cannot create export directory. Aborting.\' );
}

// empty the export dir else MySQL throws errors
$files = glob( $exportdir . \'/*\' );
if ( ! empty( $files ) ) {
    foreach( $files as $file )
        unlink( $file );
}

foreach ( $exports as $export ) {

    if ( ! isset( $tables[$export] ) )
        continue;

    if ( ! empty( $tables[$export] ) ) {
        foreach ( $tables[$export] as $table ) {

            $outfile =  sprintf( \'%s/%s_dump.sql\', $exportdir, $table );
            $sql = "SELECT * FROM {$wpdb->$table} INTO OUTFILE \'%s\'";
            $res = $wpdb->query( $wpdb->prepare( $sql, $outfile ) );

            if ( is_wp_error( $res ) )
                echo "<p>Cannot export {$table} into {$outfile}</p>";
        }
    }
}

This will create a directory in your theme folder (be sure it is writeable!) and export the posts and comments (with it\'s meta) into dump files. Use the array export to define what you want to export. I grouped the most things more or less logical (if you want to export the posts, than you should also export postsmeta and so on).

The benefit of this solution is, with the SELECT statement you can define particular stuff (e.g. only posts from a special category or only pages or only trashed posts).

Now you want to import this stuff in a new blog

global $wpdb;

$exportdir = TEMPLATEPATH . \'/export\';

$files = glob( $exportdir . \'/*_dump.sql\' );

foreach ( $files as $file ) {

    preg_match( \'#/([^/]+)_dump.sql$#is\', $file, $match );

    if ( ! isset( $match[1] ) )
        continue;

    $sql = "LOAD DATA LOCAL INFILE \'%s\' INTO TABLE {$wpdb->$match[1]};";

    $res = $wpdb->query( $wpdb->prepare( $sql, $file ) );

    if ( is_wp_error( $res ) )
        echo "<p>Cannot import data from file {$file} into table {$wpdb->$match[1]}</p>";
}

This solution is good if the posts did not contain any attachments like images. Another problem is, no users and no categories will be imported. Be sure both are created befor the import starts (or include users and categories in your export). It is a very rough method to import things, it will override existing stuff!

If you want to export the attachments also, you have to do a bit more work.

(Sidenote: Please read the complete answer and the Very Last Words at the end! This topic is not for beginners and I do not write a warning at every risky line of code)

The WordPress Importer Plugin seems to be a good way to import the whole stuff and automatically import/download the attachments. So let\'s have a look what this plugin will do.

At first the plugin ask for a XML file to upload. Then it parse the XML file and ask for an author mapping and if the attachments should be downloaded or not.

For an automatically run of the plugin we need to change some things. At first we have to skip the upload process. Thats quite easy because you can bundle the XML file with the theme and you know where the XML file is. Then we have to skip the questions that appears after uploading the XML file. We can predefine our own values and pass them to the import process.

Start with a copy of the plugin. Create a directory in your theme like autoimport and copy the files wordpress-importer.php and parsers.php to it. It is a good idea to rename the file wordpress-importer.php to something like autoimporter.php. In your theme function add a function call to trigger the automated impoprt

/**
 * Auto import a XML file
 */
add_action( \'after_setup_theme\', \'autoimport\' );

function autoimport() {
    // get the file
    require_once TEMPLATEPATH . \'/autoimport/autoimporter.php\';

    if ( ! class_exists( \'Auto_Importer\' ) )
        die( \'Auto_Importer not found\' );

    // call the function
    $args = array(
        \'file\'        => TEMPLATEPATH . \'/autoimport/import.xml\',
        \'map_user_id\' => 1
    );

    auto_import( $args );

}

At first we setup some arguments. The first thing is the complete path to the XML file. The second one is the ID of an existing user. We need this user for author mapping, this is the user where all posts will be mapped to when no new authors should be created.

Now we have to understand how the plugin works. Open your renamed plugin file and scroll down to the end. There is a function wordpress_importer_init() and an action call. Remove both, it\'s not longer needed. Now go to the top of the file and remove the plugin header (the comment at the beginning of the file). After that, rename the class WP_Importer to something like Auto_Importer, do not forget to adjust the function_exists statement and the first method WP_Importer (this is the constructor in PHP4 style).

Later we will pass the XML file direct to the class constructor, modify the first method to this

var $xmlfile = \'\';
var $map_user_id = 0;

function Auto_Importer( $args ) {

    if ( file_exists( $args[\'file\'] ) ) {

        // for windows systems
        $file = str_replace( \'\\\\\', \'/\', $args[\'file\'] );

        $this->xmlfile = $file;
    }

    if ( isset( $args[\'map_user_id\'] ) )
        $this->map_user_id = $args[\'map_user_id\'];

}

Now we have a to remove and modify some methods inside the class. The first method is the dispatch() method. This method tells you how the class works. It do three steps. At first upload the XML file, then process it and at last import the data.

Case zero is the first step, it is the greeting. This is the part that you see if you call the import at the first time. It will ask for a file to upload. Case two handles the upload and display a form for the import options. Case three finally do the import. In other words: the first two steps only ask for data we can provide ourself. We only need step 3 (case 2) and have to provide the data asked in step one and two.

In step two you see a function call to wp_import_handle_upload(). This function setup some informations about the xml file. We cannot use this function anymore because we haven\'t uploaded a file. So we have to copy and modify the function. Create a new method within the class

function import_handle_upload() {

    $url = get_template_directory_uri() . str_replace( TEMPLATEPATH, \'\', $this->xmlfile );
    $type = \'application/xml\'; // we know the mime type of our file
    $file = $this->xmlfile;
    $filename = basename( $this->xmlfile );

    // Construct the object array
    $object = array( \'post_title\' => $filename,
            \'post_content\' => $url,
            \'post_mime_type\' => $type,
            \'guid\' => $url,
            \'context\' => \'import\',
            \'post_status\' => \'private\'
    );

    // Save the data
    $id = wp_insert_attachment( $object, $file );

    // schedule a cleanup for one day from now in case of failed import or missing wp_import_cleanup() call
    wp_schedule_single_event( time() + DAY_IN_SECONDS, \'importer_scheduled_cleanup\', array( $id ) );

    return array( \'file\' => $file, \'id\' => $id );
}

And replace the function call $file = wp_import_handle_upload(); in the method handle_upload() with our new method $file = $this->import_handle_upload();

We replaced now the upload process with our own file (that should already exists). Go on and remove more unneeded methods. The methods gereet(), header() and footer() are not longer needed (header and footer only print some text) and can be removed from the class. In the dispatch() method remove the calls to this methods ($this->header() and $this->footer()).

The first step is done, now we have to care about the second step, the import options. The import options ask if it should be allowed to download the attachments and mapping the authors.

The first part is easy. Set to true if the attachments should be downloaded or false if not. The author mapping is a bit more complicated. If it is allowed to create new users (the authors from the import file), create them. If not, assign the postss to an existing user. This is been done in the method get_author_mapping(). We have to replace the $_POST data with existing data. Here we need a simple solution, so we simply map all new authors to an existing one if it is not allowed to create new users. Or simply create all new users. In the second case, be sure all new users are dummy users. If not, everytime you import them, they get an email with login and password to the new blog!! I do not explain every line of code, here is the complete rewritten method

function get_author_mapping( $map_users_id ) {
    if ( empty( $this->authors ) )
        return;

    $create_users = $this->allow_create_users();

    foreach ( (array) $this->authors as $i => $data ) {

        $old_login = $data[\'author_login\'];

        // Multisite adds strtolower to sanitize_user. Need to sanitize here to stop breakage in process_posts.
        $santized_old_login = sanitize_user( $old_login, true );
        $old_id = isset( $this->authors[$old_login][\'author_id\'] ) ? intval($this->authors[$old_login][\'author_id\']) : false;

        if ( ! $create_users ) {
            $user = get_userdata( intval($map_users_id) );
            if ( isset( $user->ID ) ) {
                if ( $old_id )
                    $this->processed_authors[$old_id] = $user->ID;
                $this->author_mapping[$santized_old_login] = $user->ID;
            }
        } else if ( $create_users ) {
            if ( ! empty($this->authors[$i]) ) {
                $user_id = wp_create_user( $this->authors[$i][\'author_login\'], wp_generate_password() );
            } else if ( $this->version != \'1.0\' ) {
                $user_data = array(
                    \'user_login\' => $old_login,
                    \'user_pass\' => wp_generate_password(),
                    \'user_email\' => isset( $this->authors[$old_login][\'author_email\'] ) ? $this->authors[$old_login][\'author_email\'] : \'\',
                    \'display_name\' => $this->authors[$old_login][\'author_display_name\'],
                    \'first_name\' => isset( $this->authors[$old_login][\'author_first_name\'] ) ? $this->authors[$old_login][\'author_first_name\'] : \'\',
                    \'last_name\' => isset( $this->authors[$old_login][\'author_last_name\'] ) ? $this->authors[$old_login][\'author_last_name\'] : \'\',
                );
                $user_id = wp_insert_user( $user_data );
            }

            if ( ! is_wp_error( $user_id ) ) {
                if ( $old_id )
                    $this->processed_authors[$old_id] = $user_id;
                $this->author_mapping[$santized_old_login] = $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 \'<br />\';
            }
        }

        // failsafe: if the user_id was invalid, default to the current user
        if ( ! isset( $this->author_mapping[$santized_old_login] ) ) {
            if ( $old_id )
                $this->processed_authors[$old_id] = (int) get_current_user_id();
            $this->author_mapping[$santized_old_login] = (int) get_current_user_id();
        }
    }
}

There is some work left to do. Adding a function auto_import() first

function auto_import( $args ) {

    $defaults = array( \'file\' => \'\', \'map_user_id\' => 0);
    $args = wp_parse_args( $args, $defaults );

    $autoimport = new Auto_Importer( $args );
    $autoimport->do_import();

}

Place this function after the class. This function miss some error handling and checking (e.g. for an empty file argument).

If you now run the class, you got a lot of error messages. The first one is, that the class is missing. This is because there is a if statement at the beginning.

if ( ! defined( \'WP_LOAD_IMPORTERS\' ) )
    return;

We have to remove it, otherwise the file would not be parsed completely. Than there are some functions that are not loaded at this point. We have to include some files.

$required = array(
    \'post_exists\'                     => ABSPATH . \'wp-admin/includes/post.php\',
    \'wp_generate_attachment_metadata\' => ABSPATH . \'wp-admin/includes/image.php\',
    \'comment_exists\'                  => ABSPATH . \'wp-admin/includes/comment.php\'
);

foreach ( $required as $func => $req_file ) {
    if ( ! function_exists( $func ) )
        require_once $req_file;
}

Basically thats all. I test this on a local installation with the test data XML from WordPress. It work for me but it is not a perfect solution for production!

And some last words on setting up some options. There are two options that can be modified by a filter:

add_filter( \'import_allow_create_users\', function() { return false; } );
add_filter( \'import_allow_fetch_attachments\', \'__return_false\' );

I think I do not have to explain it. Put this filters in your functions.php and setup true or false (first one is PHP5.3 style, second is WP style).

Very Last Words

I put alltogether in this gist. Use it at your own risk! I\'m not responsible for anything!. Please have a look at the files in the gist, I did not explain every little step here.

Thinks I haven\'t done: Set a value e.g. in the (theme) options after importing. Else the import starts every time the theme will be activated.

Maybe I will work on it in the future, clean up some things and run more tests on it.

SO网友:user32057

请允许我在这里再次介绍两件事:

(a) “我不是在问如何……我已经把那部分整理好了……”

»»随着时间的推移,我逐渐认识到,解决问题/修复的方法并不一定需要与手头的问题有某种“可见的关联”。

(b) “…我需要做什么才能把零件去掉…”“…客户是商人,所以即使是像…”

»»为什么要让客户更容易,而代价是让自己更难?我当然可以在交付物之后提供“服务”,并建立远程连接为他们提供服务[收费],而不是“…入侵导入插件…”。我的意思是,问问自己,在你目前的计划中,这是否真的值得。然而,如果您愿意付出努力,那么请尝试一下下面的代码。如果可以,则:

首先掌握基础知识,更好地理解数据库all functions-old is goldall hooks-simplify simplify

<?php 
/* I usually dump ONE line in functions.php  */
require_once (TEMPLATEPATH . \'/includes/whatever.php\');

/* and then in that loc CHECK FIRST*/
if ((is_admin() && isset($_GET[\'activated\']) && $pagenow == \'themes.php\')||(is_admin() && isset($_GET[\'upgrade\']) && $pagenow == \'admin.php\' && $_GET[\'page\'] == \'admin-options.php\')) 
{

global $wpdb, $wp_rewrite, $hey;

// create tables
your_tables();

// insert value defaults
your_values();

// insert link defaults
your_links();

// pages and tpl
your_pages();

// create category or categories
// wp_create_categories     $categories, $post_id = \'\'
// wp_create_category   $cat_name, $parent

//flush rewrite
$wp_rewrite->flush_rules();

}

// create them db tables
function your_tables() {
global $wpdb, $hey;

$collate = \'\';
if($wpdb->supports_collation()) {
if(!empty($wpdb->charset)) $collate = "DEFAULT CHARACTER SET $wpdb->charset";
if(!empty($wpdb->collate)) $collate .= " COLLATE $wpdb->collate";
}

$sql = "CREATE TABLE IF NOT EXISTS ". $wpdb->prefix . "table1_name" ." (
`id` INT(10) NOT NULL auto_increment,
`some_name1` VARCHAR(255) NOT NULL,
`some_name2` VARCHAR(255) NOT NULL,
`some_name3` LONGTEXT,
`some_name4` LONGTEXT NOT NULL,
`some_name5` VARCHAR(255) DEFAULT NULL,
`some_name6` VARCHAR(255) DEFAULT NULL,
`some_name7` DATETIME NOT NULL DEFAULT \'0000-00-00 00:00:00\',
`some_name8` DATETIME NOT NULL DEFAULT \'0000-00-00 00:00:00\',
PRIMARY KEY id  (`id`)) $collate;";

$wpdb->query($sql);


$sql = "CREATE TABLE IF NOT EXISTS ". $wpdb->prefix . "table2_name" ." (
`meta_id` INT(10) NOT NULL AUTO_INCREMENT,
`some_name1` INT(10) NOT NULL,
`some_name2` INT(10) NOT NULL,
`some_name3` VARCHAR(255) NOT NULL,
`some_name4` INT(10) NOT NULL,
PRIMARY KEY id  (`meta_id`)) $collate;";

$wpdb->query($sql);

// and so on and so forth

/* Insert default/ALL data into tables */
// BUT CHECK FIRST IF DATA EXISTS. IF = YES DONT PUSH IN ANYTHING

$sql = "SELECT field_id " . "FROM " . $wpdb->prefix . "table1_name LIMIT 1";

$wpdb->get_results($sql);

if($wpdb->num_rows == 0) {

// more code will follow
// i have to get going now

} 

?>
请注意,如果您已经与WP合作了一段时间,则无需提及BACK UP YOUR DB FIRST.

phpMyAdmin有着原始的能力,很容易把事情搞砸。

虽然一开始所需的努力可能令人望而生畏,但如果做得好,你可以让它像发条一样工作ф。。。

最后,如何在20秒内将2000行数据推送到这两个大括号内的最后两行中?

phpMyAdmin»选择左侧的数据库»选择右侧的所有表»导出▼

➝ Custom: display all options
➝ View output as text = ON
➝ Save output to a file = OFF
➝ Compression = NONE
➝ Format = SQL
➝ Dump Table = STRUCTURE & DATA
➝ Add DROP TABLE... = OFF (Important!)
➝ Syntax to use = "both of the above"

»» GO!
从下一个屏幕上,我可以将“结构”部分复制到$sql=“..”对于your_tables() 以及“数据”部分$sql 对于your_data()

对于我使用的其余WP默认值update_option(...) &;update_post_meta(...)

SO网友:chrisguitarguy

没有与register_activation_hook 对于插件,有几个hacks. 为什么?因为主题是皮肤。只有与内容显示特别相关的功能才应该放在主题中,而不是内容本身。

至于方法:使用example above 运行回调函数一次。WordPress导入程序处理XML文件有many different waysparse XML 在PHP中。选择、解析文件,然后对其执行任何操作。

SO网友:amolv

在函数中。可以检查php条件

if( isset($_GET[\'activated\']) && \'themes.php\' == $GLOBALS[\'pagenow\']) )
{ 
  // check duplicates 
   // call import class 
   //xml import code 
   // do whatever you want to 
}
一旦你的主题被激活,这将自动导入数据。

结束

相关推荐

Identifying Importer Posts

如果一个站点有100篇帖子,其中有数量不详的帖子是手动编写的,其余的帖子是使用WordPress导入器创建的,那么在没有访问远程站点或原始导入文件的情况下,我如何以编程方式识别导入的帖子?E、 g.这篇文章是由进口商工具创建的吗?