Il y a quelques jours, je vous ai expliqué comment je m’y suis pris pour migrer mon blog Dotclear vers WordPress et mettre en place une redirection des anciennes URL vers les nouvelles. Une solution relativement simple, s’appuyant sur des plug-ins existants pour WordPress et sur quelques lignes de PHP. Avec tout de même un défaut, auquel j’ai remédié ce week-end : les mots-clefs n’étaient pas migrés. C’est désormais chose faite.
N’ayant pas trouvé de solution clé en main pour effectuer cette migration, j’ai décidé d’en faire une moi même, ce qui est en fait relativement simple, même si la structure de base de WordPress est un poil plus complexe que celle de Dotclear.
Côté Dotclear, c’est simple, voir simpliste : une unique table meta contient des triplets composés de l’ID de la méta-donnée (qui est le mot-clé lui même dans notre cas), du type de la méta-donnée et de l’ID du billet auquel elle est associée. Par exemple, si le billet numéro 912 a le tag mot-clé adsl, on va trouver une ligne (‘adsl’, ‘tag’, 912) dans la table meta.
Côté WordPress, il y a une distinction entre le stockage des mots-clés et leur association à des articles. Le mécanisme s’appuie sur trois tables différentes :
- terms qui contient les mots-clés, leur slug (l’identifiant pour les reconnaitre dans l’URL), leur groupe (pas cherché à comprendre ce que c’est ^^) et un term_id unique,
- term_taxonomy qui associe un term_id avec une taxonomie (type de terme, par exemple post_tag pour un tag, category pour une catégorie), un term_taxonomy_id unique, une description et un parent optionnel, et un compteur d’occurences,
- term_relationships qui associe un term_taxonomy_id avec un ID d’article.
Si l’article numéro 912 a le mot-clé adsl (et est le seul à l’avoir), on va trouver une entrée (x, ‘adsl’, ‘adsl’, 0) dans la table terms, une entrée (y, x, ‘post_tag’, », 0, 1) dans term_taxonomy et (912, y, 0) dans term_relationships.
De ces structures découle donc l’algorithme de migration suivant :
- récupérer l’ensemble des mots-clés et la liste des billets associés dans la base Dotclear,
- insérer les mots-clés dans la base WordPress (tables terms et term_taxonomy),
- récupérer les term_taxonomy_id des mots-clés,
- faire la correspondance entre les id des billets Dotclear et ceux des articles WordPress,
- pour chaque mot-clé récupéré à l’étape 1, prendre son term_taxonomy_id trouvé à l’étape 3 et la liste des id d’articles trouvée à l’étape 4 pour insérer les entrées correspondantes dans term_relationships,
- mettre à jour le champ count de term_taxonomy en fonction du nombre d’enregistrements correspondants dans term_relationships.
Traduit en PHP, ça donne quelque choses comme ça :
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
<?php $dc2Base = "dc2base"; // nom de la base Dotclear $wpBase = "wpbase"; // nom de la base WP $sqlServer = "localhost"; // nom du serveur SQL $sqlUser = "user"; // utilisateur SQL $sqlPass = "pass"; // mot de passe SQL // Suppression des accents et des majuscules pour la création du slug // Source : http://www.weirdog.com/blog/php/supprimer-les-accents-des-caracteres-accentues.html function wd_remove_accents($str, $charset='utf-8') { $str = htmlentities($str, ENT_NOQUOTES, $charset); $str = preg_replace('#&([A-za-z])(?:acute|cedil|circ|grave|orn|ring|slash|th|tilde|uml);#', '\1', $str); $str = preg_replace('#&([A-za-z]{2})(?:lig);#', '\1', $str); // pour les ligatures e.g. 'œ' $str = preg_replace('#&[^;]+;#', '', $str); // supprime les autres caractères return strtolower($str); } $tagsdc = Array(); $tagswp = Array(); $dcid2wpid = Array(); // Recherche des mots clés DC $sql = mysql_connect($sqlServer, $sqlUser, $sqlPass); mysql_query("SET NAMES 'utf8'", $sql); $query = "SELECT meta_id, post_id FROM $dc2Base.dc2_meta WHERE meta_type = 'tag'"; $result = mysql_query($query, $sql); $nb_enr = mysql_num_rows($result); for ($i = 0; $i < $nb_enr; $i++) { $tagsdc[mysql_result($result, $i, "meta_id")][] = mysql_result($result, $i, "post_id"); } // Ajout des tags dans WP foreach ($tagsdc as $tag => $posts) { $slug = wd_remove_accents($tag); $query = "REPLACE INTO $wpBase.wp_terms (name, slug) VALUES ('$tag', '$slug')"; mysql_query($query, $sql); if (mysql_affected_rows($sql) > 0) { $id = mysql_insert_id($sql); $query = "REPLACE INTO $wpBase.wp_term_taxonomy (term_id, taxonomy) VALUES ($id, 'post_tag')"; mysql_query($query, $sql); } } // Récupération des id des tags dans WP $query = "SELECT ta.term_taxonomy_id as id, t.slug as slug FROM $wpBase.wp_terms t, $wpBase.wp_term_taxonomy ta WHERE t.term_id = ta.term_id AND ta.taxonomy = 'post_tag'"; $result = mysql_query($query, $sql); $nb_enr = mysql_num_rows($result); for ($i = 0; $i < $nb_enr; $i++) { $tagswp[mysql_result($result, $i, "slug")] = mysql_result($result, $i, "id"); } // Récupération des id des posts dans WP $query = "SELECT wp.ID as wpid, dc2.post_id as dcid FROM $wpBase.wp_posts wp, $dc2Base.dc2_post dc2 WHERE dc2.post_title = wp.post_title"; $result = mysql_query($query, $sql); $nb_enr = mysql_num_rows($result); for ($i = 0; $i < $nb_enr; $i++) { $dcid2wpid[mysql_result($result, $i, "dcid")] = mysql_result($result, $i, "wpid"); } // Association des tags aux posts foreach ($tagsdc as $tag => $posts) { if (!isset($tagswp[wd_remove_accents($tag)])) { echo "Erreur : tag $tag introuvable dans WP pour les posts ".implode(",", $posts)."<br/>"; continue; } $idwp = $tagswp[wd_remove_accents($tag)]; $postswp = Array(); foreach ($posts as $post) { if (!isset($dcid2wpid[$post])) { echo "Erreur : post $post introuvable dans WP pour le tag $tag<br/>"; continue; } $postswp[] = "(".$dcid2wpid[$post].", $idwp)"; } if (count($postswp) > 0) { $query = "REPLACE INTO $wpBase.wp_term_relationships (object_id, term_taxonomy_id) VALUES ".implode(",", $postswp); mysql_query($query, $sql); } } // Mise à jour des cardinalités $query = "UPDATE $wpBase.wp_term_taxonomy t SET t.count = (SELECT COUNT(*) FROM wp_term_relationships r WHERE r.term_taxonomy_id = t.term_taxonomy_id)"; mysql_query($query, $sql); ?> |
Si la migration de certains mots clés échoue (ce qui peut arriver notamment si le slug est déjà utilisé pour une catégorie, ce cas n’étant pas pris en compte dans le code), un message s’affiche, listant les id des billets correspondant, ce qui permettra de traiter manuellement les cas non traités automatiquement.
J’ai également mis à jour le script de redirection pour gérer la redirection des pages de mots clés et de catégories :
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
<?php $redir = "http://www.domaine.tld/wp/"; // URL de base du WP $dc2Base = "dc2base"; // nom de la base Dotclear $wpBase = "wpbase"; // nom de la base WP $sqlServer = "localhost"; // nom du serveur SQL $sqlUser = "user"; // utilisateur SQL $sqlPass = "pass"; // mot de passe SQL $mail = "user@domain.tld"; // mail de destination du rapport d'erreur $query = "index.php/"; // à remplacer par index.php? si le blog est en mode QUERY_STRING $isFeed = false; $isCommentFeed = false; $context = ""; $slug = ""; $request = urldecode($_SERVER["REQUEST_URI"]); $identifier = str_replace($query, "", $request); // Suppression des informations relatives aux flux if (strpos($identifier, "feed") !== false) { $context = "feed"; $isFeed = true; if (strpos($identifier, "comments") !== false) { $isCommentFeed = true; } $identifier = str_replace("feed", "", $identifier); $identifier = str_replace("comments", "", $identifier); $identifier = str_replace("rss2", "", $identifier); $identifier = str_replace("atom", "", $identifier); while (strpos($identifier, "//") !== false) { $identifier = str_replace("//", "/", $identifier); } } // Suppression des pages if (($pos = strpos($identifier, "page")) !== false) { $identifier = substr($identifier, 0, $pos); } if ($identifier[0] == "/") { $identifier = substr($identifier, 1); } if ($identifier[strlen($identifier) - 1] == "/") { $identifier = substr($identifier, 0, strlen($identifier) - 1); } // Traitement des posts if (strpos($identifier, "post/") === 0) { $context = "post"; $url = substr($identifier, 5); $sql = mysql_connect($sqlServer, $sqlUser, $sqlPass); $query = "SELECT wp.post_name as redir FROM $wpBase.wp_posts wp, $dc2Base.dc2_post dc2 WHERE LCASE(dc2.post_url) = LCASE('$url') AND dc2.post_title = wp.post_title"; mysql_query("SET NAMES 'utf8'", $sql); $result = mysql_query($query, $sql); $nb_enr = mysql_num_rows($result); if ($nb_enr == 1) { $slug = mysql_result($result, 0, "redir"); } } else if (strpos($identifier, "tag/") === 0) { $context = "tag"; $tag = substr($identifier, 4); $sql = mysql_connect($sqlServer, $sqlUser, $sqlPass); $query = "SELECT t.slug as redir FROM $wpBase.wp_terms t, $wpBase.wp_term_taxonomy ta WHERE t.name = '$tag' AND t.term_id = ta.term_id AND ta.taxonomy = 'post_tag'"; mysql_query("SET NAMES 'utf8'", $sql); $result = mysql_query($query, $sql); $nb_enr = mysql_num_rows($result); if ($nb_enr == 1) { $slug = mysql_result($result, 0, "redir"); } } else if (strpos($identifier, "category/") === 0) { $context = "categorie"; $cat = substr($identifier, 9); $sql = mysql_connect($sqlServer, $sqlUser, $sqlPass); $query = "SELECT t.slug as redir FROM $wpBase.wp_terms t, $wpBase.wp_term_taxonomy ta WHERE t.name = '$cat' AND t.term_id = ta.term_id AND ta.taxonomy = 'category'"; mysql_query("SET NAMES 'utf8'", $sql); $result = mysql_query($query, $sql); $result = mysql_query($query, $sql); $nb_enr = mysql_num_rows($result); if ($nb_enr == 1) { $slug = mysql_result($result, 0, "redir"); } } if ($context == "feed") { $redir = $base . "feed"; if ($isCommentFeed) { $redir .= "/comments"; } } else if ($slug != "") { if ($context == "tag" || $context == "categorie") { $redir = $base . $context . "/" . $slug; if ($isFeed) { $redir .= "/feed"; } } else { $redir = $base . $slug; if ($isCommentFeed) { $redir .= "/feed"; } } } if (isset($redir)) { header("HTTP/1.1 301 Moved Permanently"); header("Location: $redir"); exit; } if ($mail != "") { mail($mail, "Problème de redirection DC2 vers WP", "Aucune correspondance trouvée pour l'URL $request"); } define('DC_BLOG_ID','default'); require 'inc/public/prepend.php'; ?> |
MErci pour ce script
je l’ai un peu modifié car il y avait quelques bugs
mais au final, cela ne marche pas non plus..
il n’arrive pas à associer les etiquettes et les posts
de plus, les slugs ne sont pas correctement insérés non plus
et la j’avoue que mes connaissances php ne suffisent pas.. 🙁
Ça avait bien fonctionné à l’époque pour la migration d’Infobidouille, mais depuis le temps c’est possible que les évolutions respectives de Dotclear et de WordPress aient rendu ce script inutilisable 🙁