web-dev-qa-db-fra.com

Importer un fichier XML Wordpress à partir de Functions.php

Je développe un thème qui a une méthode différente d’ajout de contenu et l’installation par défaut de Wordpress n’affichera donc aucun contenu pour cette raison. Je me demandais s'il était possible d'importer automatiquement un fichier XML au moyen d'une fonction interne et/ou de points d'ancrage après l'activation du thème.

L'utilisateur installe le thème > L'utilisateur active le thème > Un code en arrière-plan charge un fichier XML et en effectue une importation silencieuse

Actuellement, pour importer un fichier XML, vous devez installer le plug-in d'importateur Wordpress pour Wordpress, puis importer manuellement le fichier, sélectionner un utilisateur pour associer le contenu importé et décider si vous souhaitez importer des pièces jointes multimédias. Je trouve cette étape trop compliquée pour les types de clients que je cible et je voudrais effectivement éliminer le besoin de cette étape.

J'ai creusé dans le script d'importateur Wordpress et il y a beaucoup d'appels de fonction. Que dois-je faire pour supprimer les parties où la saisie de l'utilisateur est requise et importer un fichier à l'aide de la classe et de son répertoire de méthodes? Je ne sais pas vraiment par où commencer.

Mes clients étant des artisans, même quelque chose d'aussi simple que d'importer un fichier XML les attache et ils n'ont pas le temps de le faire. Il y a donc de la place pour des erreurs, en particulier s'ils essaient d'importer plusieurs fois, ce qui entraîne la duplication de pages.

Merci d'avance.

Edit/Clarification

Il semble y avoir beaucoup de confusion ici. Je ne demande pas comment vérifier si un thème a été activé, j'ai trié cette partie. Je demande comment je procéderais pour analyser un fichier d'importation XML et l'importer automatiquement sans effort de l'utilisateur. Je veux essentiellement automatiser le plugin d'importation Wordpress que vous pouvez déjà utiliser pour importer manuellement le fichier XML, choisir l'auteur, choisir de télécharger et d'importer des pièces jointes dans mon functions.php.

Au lieu d'avoir besoin d'un plugin ou de demander à mes clients qui n'ont pas de connaissances en informatique ni le désir d'apprendre à le faire en utilisant le plugin.

6
Dwayne Charrington

Votre question est un peu spécifique si vous "seulement" voulez importer automatiquement certains articles/pages. Pour ce faire, il existe d'autres moyens que d'utiliser un fichier d'exportation XML.

Si vous avez des publications textuelles, vous devez utiliser LOAD DATA INFILE . Au début, vous devez exporter vos messages.

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>";
        }
    }
}

Cela créera un répertoire dans votre dossier de thème (assurez-vous qu'il est accessible en écriture!) Et exportera les publications et les commentaires (avec sa méta) dans des fichiers de vidage. Utilisez le tableau export pour définir ce que vous voulez exporter. J'ai regroupé la plupart des choses plus ou moins logiques (si vous voulez exporter les publications, vous devez également exporter les publications, etc.).

L'avantage de cette solution est que, avec l'instruction SELECT, vous pouvez définir des éléments particuliers (par exemple, uniquement les publications d'une catégorie spéciale ou uniquement les pages ou uniquement les publications supprimées).

Maintenant, vous voulez importer ces choses dans un nouveau 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>";
}

Cette solution est bonne si les publications n'ont pas contiennent des pièces jointes telles que des images. Un autre problème est qu'aucun utilisateur et aucune catégorie ne seront importés. Assurez-vous que les deux sont créés avant le début de l'importation (ou incluez des utilisateurs et des catégories dans votre exportation). C'est une méthode très grossière pour importer des choses, cela remplacera les choses existantes!

Si vous souhaitez également exporter les pièces jointes, vous devez effectuer un peu plus de travail.

(Sidenote: Veuillez lire la réponse complète et les tous derniers mots à la fin! Ce sujet n'est pas destiné aux débutants et je n'écris pas d'avertissement pour chaque ligne de code présentant un risque)}

WordPress Importer Plugin semble être un bon moyen d’importer tout le contenu et d’importer/télécharger automatiquement les pièces jointes. Voyons donc ce que ce plugin va faire.

Au début, le plugin demande un fichier XML à télécharger. Ensuite, il analyse le fichier XML et demande un mappage d'auteur et indique si les pièces jointes doivent être téléchargées ou non.

Pour que le plugin fonctionne automatiquement, nous devons changer certaines choses. Au début, nous devons ignorer le processus de téléchargement. C’est assez facile car vous pouvez associer le thème au fichier XML et vous savez où se trouve le fichier XML. Ensuite, nous devons ignorer les questions qui apparaissent après le téléchargement du fichier XML. Nous pouvons prédéfinir nos propres valeurs et les transmettre au processus d'importation.

Commencez avec une copie du plugin. Créez un répertoire dans votre thème tel que autoimport et copiez-y les fichiers wordpress-importer.php et parsers.php. C'est une bonne idée de renommer le fichier wordpress-importer.php en quelque chose comme autoimporter.php. Dans votre fonction thème, ajoutez un appel de fonction pour déclencher l'import automatisé.

/**
 * 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 );

}

Au début, nous établissons des arguments. La première chose est le chemin complet du fichier XML. Le second est l’ID d’un utilisateur existant . Nous avons besoin de cet utilisateur pour le mappage des auteurs, c’est l’utilisateur auquel tous les messages seront mappés quand aucun nouvel auteur ne doit être créé.

Nous devons maintenant comprendre comment fonctionne le plugin. Ouvrez votre fichier de plug-in renommé et faites défiler jusqu'à la fin. Il existe une fonction wordpress_importer_init() et un appel d'action. Supprimez les deux, ce n'est plus nécessaire. Allez maintenant en haut du fichier et supprimez l’en-tête du plugin (le commentaire au début du fichier). Après cela, renommez la classe WP_Importer en quelque chose comme Auto_Importer, n'oubliez pas de régler l'instruction function_exists et la première méthode WP_Importer (il s'agit du constructeur de style PHP4).

Plus tard, nous allons passer le fichier XML directement au constructeur de la classe, modifier la première méthode en ceci

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'];

}

Nous avons maintenant pour supprimer et modifier certaines méthodes de la classe. La première méthode est la méthode dispatch(). Cette méthode vous explique comment fonctionne la classe. Il fait trois étapes. Commencez par télécharger le fichier XML, puis traitez-le et importez enfin les données.

Le cas zéro est la première étape, c'est la salutation. C'est la partie que vous voyez si vous appelez l'importation la première fois. Il vous demandera un fichier à télécharger. Le deuxième cas gère le téléchargement et affiche un formulaire pour les options d'importation. Le cas trois enfin faire l'importation. En d'autres termes: les deux premières étapes ne demandent que des données que nous pouvons nous fournir nous-mêmes. Nous avons seulement besoin de l'étape 3 (cas 2) et devons fournir les données demandées aux étapes 1 et 2.

À la deuxième étape, vous appelez la fonction wp_import_handle_upload(). Cette fonction configure des informations sur le fichier xml. Nous ne pouvons plus utiliser cette fonction car nous n'avons pas téléchargé de fichier. Nous devons donc copier et modifier la fonction. Créer une nouvelle méthode dans la classe

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 );
}

Et remplacez l'appel de fonction $file = wp_import_handle_upload(); dans la méthode handle_upload() par notre nouvelle méthode $file = $this->import_handle_upload();

Nous avons maintenant remplacé le processus de téléchargement par notre propre fichier (qui devrait déjà exister). Continuez et supprimez les méthodes inutiles. Les méthodes gereet(), header() et footer() ne sont plus nécessaires (l'entête et le pied de page n'impriment que du texte) et peuvent être supprimées de la classe. Dans la méthode dispatch(), supprimez les appels à ces méthodes ($this->header() et $this->footer()).

La première étape est terminée, nous devons maintenant nous préoccuper de la deuxième étape, les options d'importation. Les options d'importation demandent s'il faut autoriser le téléchargement des pièces jointes et le mappage des auteurs.

La première partie est facile. Définissez la valeur sur true si les pièces jointes doivent être téléchargées ou sur false sinon. Le mappage de l'auteur est un peu plus compliqué. S'il est autorisé à créer de nouveaux utilisateurs (les auteurs à partir du fichier d'importation), créez-les. Sinon, attribuez les publications à un utilisateur existant. Cela a été fait dans la méthode get_author_mapping(). Nous devons remplacer les données $_POST par des données existantes. Ici, nous avons besoin d’une solution simple, nous allons donc simplement mapper tous les nouveaux auteurs sur un auteur existant s’il n’est pas permis de créer de nouveaux utilisateurs. Ou créez simplement tous les nouveaux utilisateurs. Dans le second cas, assurez-vous que tous les nouveaux utilisateurs sont des utilisateurs factices. Sinon, chaque fois que vous les importez, ils reçoivent un email avec le login et le mot de passe du nouveau blog !! Je n'explique pas chaque ligne de code, voici la méthode complète de réécriture

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();
        }
    }
}

Il reste du travail à faire. Ajouter une fonction 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();

}

Placez cette fonction after la classe. Cette fonction manque une partie de la gestion et de la vérification des erreurs (par exemple pour un argument de fichier vide).

Si vous exécutez maintenant le cours, vous aurez beaucoup de messages d'erreur. Le premier est que la classe est manquante. En effet, il existe une déclaration if au début.

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

Nous devons le supprimer, sinon le fichier ne serait pas complètement analysé. Ensuite, certaines fonctions ne sont pas chargées à ce stade. Nous devons inclure certains fichiers.

$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;
}

En gros c'est tout. Je teste cela sur une installation locale avec les données test XML de WordPress. Cela fonctionne pour moi mais ce n'est pas une solution parfaite pour la production!

Et quelques derniers mots sur la configuration de certaines options. Il existe deux options pouvant être modifiées par un filtre:

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

Je pense que je n'ai pas à l'expliquer. Mettez ces filtres dans votre functions.php et configurez true ou false (le premier est du style PHP 5.3, le second est du style WP).

Derniers mots

Je mets tous ensemble dans ce Gist . Utilisez-le à vos risques et périls! Je ne suis responsable de rien! . S'il vous plaît jeter un oeil aux fichiers dans le Gist, je n'ai pas expliqué chaque petite étape ici.

Je pense ne pas l'avoir fait: définissez une valeur, par exemple. dans les options (thème) après l'importation. Sinon, l'importation commence chaque fois que le thème sera activé.

Peut-être que je vais y travailler à l'avenir, nettoyer certaines choses et faire plus de tests dessus.

18
Ralf912

Permettez-moi de réintroduire 2 choses ici:

(a) "Je ne demande pas comment ... j'ai trié cette partie ..."

"" Au fil du temps, j'ai appris à accepter le fait que l'approche des problèmes/solutions ne nécessite pas nécessairement une "association visible" avec le problème en question.

(b) "... devrais-je faire pour enlever les pièces ..." "... les clients sont des commerçants, alors même quelque chose d'aussi simple que ..."

"" Pourquoi simplifier les choses pour le client au prix de rendre les choses difficiles pour vous? Je pourrais certainement offrir des "services" après les produits livrables et établir une connexion à distance pour le faire pour eux [facturable], au lieu de "... piratage du plugin d'importation ...". Je veux dire, demandez-vous si cela en vaut vraiment la peine dans votre schéma actuel. Cependant, si vous êtes prêt à faire un effort, essayez le code ci-dessous. Si vous le pouvez, alors:

Je suis d'accord avec chrisguitarguy et amolv ci-dessus.

Comme le souligne chris, le nombre de façons d’atteindre un résultat est multiple. Ceci est juste un. Bien qu'il puisse être laborieusement long, référez-vous aux deux dernières lignes avant toute autre chose.

<?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

} 

?>

REMARQUE

  • Si vous êtes avec WP pendant un certain temps, il est inutile de mentionner SAUVEGARDEZ VOTRE DB EN PREMIER .

  • phpMyAdmin a le pouvoir brut et il est très facile de tout bousiller avec soin.

  • Bien que l’effort requis puisse sembler décourageant au départ, si vous le faites bien, vous pourriez le faire fonctionner comme une horloge ...

Finalement

Comment insérer 2000 lignes de données en 20 secondes dans les deux dernières lignes entre ces deux accolades?

phpMyAdmin "Sélectionnez DB à gauche" "Sélectionnez toutes les tables à droite" "Export ▼

➝ 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!
  • À partir de l'écran suivant, je pourrais copier la partie 'STRUCTURE' dans $ sql = "...." pour your_tables() et la partie 'DATA' dans $sql pour your_data().

  • Pour le reste de WP par défaut, j'utilise update_option(...) & update_post_meta(...)

4
user32057

dans functions.php la condition peut être vérifiée

if( isset($_GET['activated']) && 'themes.php' == $GLOBALS['pagenow']) )
{ 
  // check duplicates 
   // call import class 
   //xml import code 
   // do whatever you want to 
}

Dès que votre thème est activé, les données sont automatiquement importées.

0
amolv

Il n'y a pas d'équivalent thème de register_activation_hook pour les plugins - il y a quelques hacks . Pourquoi? Parce qu'un thème est une peau. Seules les fonctionnalités spécifiquement liées à l'affichage du contenu doivent figurer dans un thème, pas le contenu lui-même.

Pour ce qui est de savoir comment: utilisez l’exemple ci-dessus pour exécuter une fonction de rappel une fois. L'importateur WordPress fonctionne sur des fichiers XML. Il existe plusieurs plusieursdifférentesfaçons à analyser XML en PHP. Faites votre choix, analysez le fichier, faites ce que vous voulez avec.

0
chrisguitarguy