Régénération de la table wikini_links
Cette page a pour but trouver une solution pour permettre à un utilisateur dont la table wikini_links a été perdue ou endommagée de la régénérer [facilement].
Solution purement MySQL
Une approche
MySQL du problème me parait préférable, car je pense que s'orienter directement vers un script php risque d'être fort lourd, surtout en quantité de requêtes et peut-être en mémoire utilisée (pour de très gros wikis). Je vais à présent détailler ma démarche:
Tout d'abord on peut constater qu'il sera impossible de faire tout en une seule requête, et même probablement avec juste quelques requêtes, et ce pour les raisons suivantes:
- Si une page possède des liens vers des pages n'existant pas, ceux-ci sont tout de même stockés dans la base de données (logique puisque les pages en question seront peut-être créées un jour). MYSQL ne permet vraisemblablement pas d'extraire une chaine correspondant à un masque d'une autre chaine (comme le feraient les fonctions preg_match() et preg_match_all() de php)
- Toute page possède un lien vers les pages dont un lien se trouve dans l'entête du site. Normalement il y a moyen d'insérer facilement toutes ces entrées avec une requête dans laquelle on spécifie quelle est la page qui joue le rôle de l'entête (s'il y en a une...). Il y a aussi moyen de le faire en créant une table temporaire (CREATE TEMPORARY TABLE tbl_name ...) qui contiendrait la liste de tous les liens à générer pour toutes les pages.
- La cause du point précédent est la suivante: toute page "hérite" des liens de toute page qu'elle inclut (ActionInclude). Reproduire cet héritage avec un INSERT ... SELECT... est impossible puisque d'une part il est interdit d'écrire dans la table depuis laquelle on est en train de lire jusque dans la version 4.0.14 de MySQL, et d'autre part il faudrait d'abord s'assurer que toutes les pages inclues ont déjà leurs liens enrégistrés (or il peut y avoir des inclusions dans les pages inclues...). Il serait cependant possible de passer outre en exécutant une même requête plusieurs fois jusqu'à ce qu'elle ne fasse plus rien. (elle rechercherait les occurences de "{{include page="Tag"}}" via REGEXP)
- Le point précédent peut lui aussi être généralisé: toutes les actions qui génèrent des liens ne peuvent être évaluées par MySQL facilement. Seuls les liens qui sont en même temps des paramètres de ces actions seront facilement repèrables. J'imagine qu'il doit être possible de régénérer les liens engendrés par chaque action mais le problème devrait alors être traité plus "individuellement".
En conclusion, ce qu'il est possible de faire en une seule requête:
- Lier toutes les pages à leur propriétaire
- Lier toutes les pages à leur dernier éditeur
- Lier toutes les pages aux pages dont elles possèdent des références directes et qui existent. Pour celles qui n'existent pas je pense qu'il faudra obligatoirement passer par un script php qui risque d'être extrêmement lourd... (établir la liste de toutes les pages, extraire de chaque page tous les NomWiki et regarder s'ils font partie de la liste, écrire une grosse requête d'insertion, en faisant gaffe de pas réinsérer un lien existant...)
- Régénérer tous ces liens pour toutes les versions ou uniquement pour la dernière. [Les liens ne concernent (actuellement) que la dernière version. Voir la première ligne de la fonction WriteLinkTable dans wakka.php. -- ProgFou]
Pour ce faire, je propose la requête suivante (à tester encore):
- INSERT IGNORE INTO wikini_links SELECT DISTINCT a.tag from_tag, b.tag to_tag FROM wikini_pages a, wikini_pages b WHERE a.latest='Y' AND b.latest='Y' AND (a.tag=b.tag OR b.tag=a.owner OR b.tag=a.user OR (a.body REGEXP CONVERT(CONCAT('[[:<:]]', b.tag, '[[:>:]]'), BINARY)));
- (Normalement avec le DISTINCT, le IGNORE est inutile, sauf dans le cas où la table ne serait pas vide au départ. Pour travailler sur toutes les versions, enlevez la partie AND b.latest='Y') [Vu ma remarque précédente sur la version concernée, tu peux lancer une requête de vidage avant ta requête d'insertion. -- ProgFou]
Pour les tests je vous suggère de créer une table wikini_links_test ayant la même structure que wikini_links, et d'effectuer la requête précédente modifiée en conséquence. Dès lors on pourra effectuer des tests de comparaison entre les deux tables:
- Tester les liens qui n'ont pas été régénérés:
- SELECT w.from_tag, w.to_tag FROM wikini_links w, wikini_pages p LEFT JOIN wikini_links_test x ON x.from_tag=w.from_tag AND x.to_tag=w.to_tag WHERE x.from_tag IS NULL AND p.tag=w.to_tag AND p.latest='Y'
- NB.: on supprime pas mal de cas en ajoutant dans le WHERE la condition AND w.to_tag NOT IN ( liste des pages liées à l'entête séparées par une virgule )
- Chez moi cela donne quelques liens générés par des actions, principalement l'utilisation de l'ActionTrail. Il y d'ailleurs mystérieusement une ligne ActionTrail | ListeDesActionsWikiNi. C'est la seule ActionTrail dont il manque le lien avec le parent et je ne comprends pas pourquoi... (comme expliqué plus haut, ce lien aurait dû être régénéré du fait que le tag apparait dans la source sous la forme {{include page="ListeDesActionsWikiNi"}}. Le plus étonnant est que c'est le seul qui pose problème ! Même parmi la ListeDesActionsWikiNi)
- Tester les liens qui ont été générés par erreur:
- SELECT * FROM wikini_links_test w LEFT JOIN wikini_links x ON x.from_tag=w.from_tag AND x.to_tag=w.to_tag WHERE w.from_tag IS NUL
- Cette requête ne renvoie chez moi aucun résultat, ce qui est normal.
--
LordFarquaad
Solution PHP
Une solution PHP n'est pas forcément absurde dans le sens où ce besoin de regénération est sensé être très rare. De plus, utiliser une solution
MySQL nous fait dépendre des contraintes de ce langage et pourrait nous empêcher de migrer vers un autre type de base de données plus tard. Plus encore, la génération des liens
WikiNi nécessite de connaître leur mode de création qui est intimement lié au language
WikiNi ; Une solution
MySQL nous imposerait donc d'intégrer la grammaire
WikiNi autant coté PHP que
MySQL. Une solution PHP me semble donc préférable en ce qui me concerne. --
ProgFou
Quelques pistes :
- créer un seul script qui fait tout le travail : vider la table des liens, récupérer la liste des pages existantes puis regénérer les liens pour chaque page, une par une ; comme l'a fait remarquer LordFarquaad, cela risquerait d'être lourd, mais d'un autre coté on ne lancerait ce script qu'une seule fois ;
- créer un handler de page, par exemple /relink, qui recréerait les liens uniquement pour cette page ; cette solution serait encore plus lourde car elle nécessiterait une requête web par page et ne permettrait pas de faire un traitement tenant compte de contraintes globales, mais elle aurait pour avantage de permettre la regénération uniquement pour un ensemble de page et pourrait éventuellement être appelé par n'importe qui (sans paramètre, donc sans danger) ; ce handler pourrait d'ailleurs n'être qu'une modification du handler /edit en lui ajoutant un paramètre &relink.
N'oublions pas tout de même que, avec le code de
WikiNi, la regénération des liens d'
une page est aussi simple que ceci (voir
handlers/page/edit.php) :
<?php
$this->ClearLinkTable();
$this->StartLinkTracking();
$dummy = $this->Header();
$dummy .= $this->Format($body);
$dummy .= $this->Footer();
$this->StopLinkTracking();
$this->WriteLinkTable();
?>