WordPress: créer automatiquement des articles

Comme annoncé en début d’année, je me suis débarrassé du plugin ZdMultilang, malheureusement abandonné depuis plusieurs mois.
Je pensais, dans un un premier temps, créer manuellement les articles, en copiant/collant leur contenu traduit. Mais traduire plus de 100 articles et pages, à la main, m’aurait pris des journées entières, surtout si l’on considère tous les paramètres à prendre en compte, comme les catégories, les mots-clés, les pièces jointes, …
Je me suis donc lancer dans la création automatique des articles. On peut également parler de clonage d’articles.

L’objectif n’est pas ici de fournir une solution exhaustive, mais de donner des pistes, sous forme de morceaux de codes.
Les opérations prévues étaient

  • Extraire de la base de données les traductions (ils se trouvent dans la table wp_zd_ml_trans),
  • Créer les articles dans WordPress,

De cette façon, le blog contiendra les articles en français, mais également leur traduction.

Etape 1: création des articles

Il est relativement facile, avec WordPress, de créer un article automatiquement. La fonction a utiliser est $post_id = wp_insert_post($args).
Les paramètres sont nombreux. Les plus intéressants sont les suivants:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
array(
	'post_status' 		=> 'draft',
	'post_type' 		=> 'post',
	'ping_status' 		=> get_option('default_ping_status'),
	'comment_status 	=> get_option('default_comment_status'),
	'post_parent' 		=> 0,
	'menu_order' 		=> 0,
	'guid' 			=> '', ,
	'post_excerpt' 		=> '',
	'post_content' 		=> '',
	'post_title' 		=> '',
	'post_date'		=> '',
	'post_modified'	=> '',
	'tax_input'		=> array()
);

Avec cette fonction, nous pouvons donc ébaucher notre fonction de conversion:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
global $wpdb;
// On récupère les informations sur les traductions
$sql = $wpdb->prepare('select ID,post_content,post_excerpt,post_title,post_status FROM '.$wpdb->prefix.'zd_ml_trans');
$results = $wpdb->get_results($sql);
 
// On parcourt la liste des traductions
foreach ($results as $result) {
	// On récupère les infos de l'article d'origine
	$french_post = get_post($result->ID);
 
	//Création du nouvel article
	$post = array(	'post_status' 		=> 'publish',
			'post_type' 		=> 'post',
			'ping_status' 		=> 'closed',
			'comment_status' 	=> 'closed',
			'post_author' 		=> $result->post_author,
			'post_excerpt' 		=> $result->post_excerpt,
			'post_content'		=> $result->post_content,
			'post_title' 		=> $result->post_title,
			'post_date'		=> $french_post->post_date,
			'post_modified'	=> $french_post->post_date,
	);
	$post_ID = wp_insert_post( $post );
	if (is_wp_error($post_ID)) {
		echo "Error";
	}
	else {
		echo "Post successfully created";
	}
} // End of foreach

En quelques secondes, nous pouvons ainsi créer des centaines d’articles à partir de leur traduction.
Ces articles ainsi crées ne sont pas exploitables directement: ils ne sont pas classés (pas de catégories, pas de mots-clés, …), ils ne disposent d’aucune des valeurs associées à l’article d’origine (vignette par exemple).

Etape 2: taxonomies, et méta-valeurs

Il faut donc améliorer notre code, en y ajoutant ces éléments.

Occupons-nous d’abord des taxonomies. Le code est le suivant:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	// On récupère la liste des taxonomies actives
	$tax_input = array();
	$tax_list  = get_taxonomies();
	foreach ($tax_list as $tax) {
		// Pour chaque taxonomies, on récupère les termes qui sont lies à l'article
		$term_array = wp_get_object_terms($result->ID, $tax);
		// Pour chacun des termes ...
		foreach ($term_array as $term) {
			// On calcule le term_id (cas particulier pour les catégories)
			$term_id = ($tax == 'category' ? $term->term_id : $term->slug);
			// On stocke l'id
			if (! isset($tax_input[$tax])) $tax_input[$tax] = $term_id;
			else $tax_input[$tax] = $tax_input[$tax].','.$term_id;
		} // End of for each term
	} // End of for each taxonomy

Le résultat obtenu est un tableau (array), qui a la structure suivante:

1
2
3
array(
	Identifiant de taxonomie =>  "identifiant de terme, identifiant de terme, ...",
	Identifiant de taxonomie =>  "identifiant de terme, identifiant de terme, ...",

Exemple:

1
2
3
array(
	"category" =>  "techno",
	"post_tag" =>  "wordpress, plugin",

La variable $tax_input doit être ajoutée aux paramètres de la fonction wp_insert_posts($args).

1
2
3
4
5
$post = array(	'post_status' 	=> 'publish',
		'... ...'	=> ' ... ...',
		'tax_input'	=> $tax_input
	);
	$post_ID = wp_insert_post( $post );

Après les taxonomies, abordons maintenant les « méta-valeurs »: Il faut récupérer celles de l’article d’origine, et les attacher au nouvel article.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (is_wp_error($post_ID)) {
	echo "Error";
}
else {
	// Récupération des valeurs
	$all_meta_keys = get_post_custom_keys( $result->ID );
	// Parcours des valeurs
	foreach ($all_meta_keys as $key) {
	// Filtrage (inutile de garder les valeurs _edit_last, et _edit_lock
	if (strpos($key, '_edit_') === FALSE) {
		// Ajout au nouvel article
		$value = get_post_meta($id, $key, TRUE);
		add_post_meta($post_ID, $key, $value);
	}
}

Cette opération doit se faire une fois l’article crée.
En étant systématique, le risque de perdre de l’information est faible. Par contre, il convient de faire un audit avant la migration, afin d’identifier clairement les valeurs qui passeront d’un article à un autre. Certaines sont certainement inutiles. Cet audit permettra donc de mieux filtrer les valeurs.

Etape 3: Finition

Jusqu’à présent, nous nous sommes intéressés au contenant (l’article lui-même, sa classification), mais pas au contenu.
Hors dans un article plusieurs choses sont à faire et/ou à vérifier. Par exemple:

  • Si des shortcodes sont utilisés, avec des références à des articles ou des pièces jointes, il faut les modifier,
  • Pour des raisons pratiques, il peut être intéressant d’ajouter un lien entre l’article d’origine, et sa traduction (et réciproquement).

Il nous faut, pour tout cela, modifier le contenu de l’article.

Deux exemples de shortcodes peuvent être relativement facilement traités: eg-attachments, et gallery.
Les pièces jointes sont attachées aux articles d’origine. Pour les visualiser avec l’un de ces deux shortcodes, dans les articles traduits, il faut obligatoirement utiliser l’attribut id, en spécifiant l’article d’origine.

Une approche simpliste serait donc:

1
2
$result->post_content = str_replace('[ attachments ', '[ attachments id='.$result->ID, $result->post_content);
$result->post_content = str_replace('[ gallery ', '[ gallery id='.$result->ID, $result->post_content);

Je dis « approche simpliste », parce que si ces shortcodes contiennent des attributs particuliers (include, exclude, par exemple), nous ne sommes pas assurés d’obtenir de bons résultats lors de la visualisation des articles.

Une solution plus complexe consisterait à utiliser les expressions régulières, pour la détection des shortcodes, et l’analyse de leurs attributs. A noter qu’il est inutile de se plonger dans les regex puisque la fonction WordPress get_shortcode_regex nous donne l’expression régulières nécessaires.

Là encore, un audit avant la migration, vous permettra de savoir assez vite, si vous avez besoin de la version simple, ou d’une version plus complexe.

Pour faire les liens entre les articles, j’ai également choisi une approche simple: je place le lien immédiatement après la balise <--more-->. Le script ressemble donc à quelque chose comme cela:

1
2
$string  = '<p>This post is a English translation of <a href="'.get_permalink($result->ID).'">'.htmlspecialchars($result->post_title).'</a></p>';
$result->post_content = str_replace('<--more-->', '<--more-->' . $string, $result->post_content);

Le lien dans ce sens-là (de l’article traduit, vers l’article d’origine), ne pose pas de problème. Par contre, dans l’autre sens (de l’article d’origine, vers la traduction, qui est le nouvel article), les choses se compliquent un peu, si vous choisissez de ne pas publier immédiatement le nouvel article (post_status!= »publish »).
En effet, la fonction get_permalink ne marche pas, et il est plus compliqué d’obtenir le bon lien.

Je ne sais pas si ma méthode est correcte, mais voici ce que j’ai trouvé:

1
2
list($new_post_permalink, $new_post_name) = get_sample_permalink($post_ID);
$new_post_permalink = str_replace(array('%pagename%','%postname%'), $new_post_name, $new_post_permalink);

Conclusion

Voici donc quelques pistes à creuser, si vous envisagez vous aussi, de désinstaller une extension de multilinguisme, et conserver les articles traduits. J’insiste sur l’importance de la préparation. Faire un script de ce type, peut, en apparence, donner de très bons résultats, assez rapidement. Mais un audit, avant migration, permettra de détecter ce que vous n’aviez pas prévu dans le script. Deux exemples personnels:

  • la balise <--more--> n’était pas toujours correctement orthographiée dans les articles traduits. La conséquence est que les liens n’étaient pas insérés, ou pas au bon endroit
  • Certains de mes shortcodes [ gallery] contenaient l’attribut include. Il était donc inutile d’ajouter l’attribut id.
    Un audit n’est pas forcément très long, et peut prendre simplement la forme de requête SQL dans la base de données, pour identifier, par exemple, ou sont vos shortcodes, et quels attributs ils ont.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *