Wikini

BoingBoing

PagePrincipale :: DerniersChangements :: DerniersCommentaires :: ParametresUtilisateur :: Vous êtes ec2-3-22-181-209.us-east-2.compute.amazonaws.com

Action "Boing Boing" version 0.3


Description

Le but de ce script est de donner un aperçu rapide des derniers changements ayant eu lieu sur le Wiki. En cela il est similaire à l'action DerniersChangements. Cependant il diffère en plusieurs points:
Attention: cette action est gourmande en temps machine. Optimisation en cours.

Utilisation

Insérez un appel de ce type dans une page vide
{{boingboing}}

Effet

A coté de chaque page, un barre de couleur apparait. Une petite barre correspond a une petite page, et une grosse barre a une grosse. La partie verte veut dire que la page a reçu récemment des modifications; et une barre entièrement bleue représente une page où il ne se passe rien de neuf.

Installation


Démonstration

http://valentin.deleplace.free.fr/wikini/wakka.php?wiki=BoingBoing03

Code source


<?php
/*
Version 0.3 de l'action "Boing Boing" pour WikiNi
Copyright  2005 Valentin Deleplace
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
 Explications

 Le but de ce script est de donner un aperçu rapide des derniers
 changements ayant eu lieu sur le Wiki. En cela il est similaire
 à l'action DerniersChangements. Cependant il diffère en plusieurs
 points:
    -Il tente d'afficher le plan du site sous forme hiérarchique,
     à partir de la page d'accueil, en suivant récursivement les
     liens. En cela il ressemble a PlanDuSite. Attention, ce n'est
     là qu'un "arbre couvrant", en vrai le wiki est un bon gros
     graphe cyclique bien touffu.
    -La date de dernière modification n'est pas le seul critère
     pris en compte. La taille de la page concernée, ainsi que
     le volume qui a été modifié sont pris en compte.
    -Les modifications pour chaque page ont une importance
     décroissante avec leur age. Les changements datant de moins
     de 3 jours sont les plus significatifs, ceux de 2 semaines
     un peu moins, ceux de 2 mois beaucoup moins, et ceux de plus
     de 3 mois ne sont pas pris en compte.
    -Le rendu est plus visuel, car un graphisme s'affiche à côté du
     nom de chaque page, d'autant plus gros que la page est grande,
     et d'autant plus voyant que la page a subi des modifications
     recentes. Ce graphisme peut être un gif animé, ou bien une
     barre horizontale, etc.
    -Les pages ne sont affichées qu'une seule fois
    -Les pages ayant un sommaire référencé par l'action 'trail'
     apparaissent bien rangées sous leur page de sommaire.
*/


//vérification de sécurité
if (!eregi("wakka.php"$_SERVER['PHP_SELF'])) {
    die (
"acc&egrave;s direct interdit");
}

$wiki $this;

/*
 Comme toutes les pages du site sont censées être affichées, je charge
 en une seule requête toutes les informations "légères" qui peuvent être
 utiles, mais pas le contenu textuel de la page.
 Cela suppose qu'en MySQL, la requête LENGTH(body) se fait en temps O(1)
 et non en temps proportionnel à la longueur de la page.
*/
$req "SELECT id, tag, time, owner, LENGTH(body) as taille
 FROM "
.$wiki->config["table_prefix"]."pages
 WHERE latest='Y'" 
;
$resul $wiki->LoadAll$req );
$pages = array();
foreach( 
$resul as $page ){
  
$tag $page["tag"];
  
$pages$tag ] = array( "id" => $page["id"],
              
"time" => $page["time"],
              
"owner" => $page["owner"],
              
"taille" => $page["taille"] );
}

// Chargement de tous les droits en lecture
$username "";
if( 
$user $this->GetUser() ){
  
$username $user["name"];
 }
$req "SELECT page_tag, list FROM ".$wiki->config["table_prefix"]."acls WHERE privilege='read'";
$resul $wiki->LoadAll$req );
foreach( 
$resul as $page ){
  
$tag $page["page_tag"];
  if( 
$pages[$tag]["owner"] == $username ){
    
$pages$tag ][ "lectureOK" ] = 1;
    continue;
  }
  
$liste $page["list"];
  
$autorises explode("\n"$liste);
  if( 
in_array($username$autorises)
      || 
in_array("*"$autorises)
      || (
$username && in_array("+",$autorises)) ){
    
$pages$tag ][ "lectureOK" ] = 1;
  }
}
// Pourquoi la page d'accueil est pas dans la table acls?? mystère
$pages$this->config["root_page"] ]["lectureOK"] = 1;


/*
 Renvoie de facon brute les lignes de différence entre les deux textes.
 Cette fonction sert plutot à comparer 2 versions d'un même texte...
 Le résultat n'est pas vraiment destiné à être formaté, mais plutôt
 analysé (en dessous il n'est utilisé que pour sa longueur).
*/
function pbdiff($texteA,$texteB,$wiki){
  
$bodyA explode("\n"$texteA);
  
$bodyB explode("\n"$texteB);
  
$added array_diff($bodyA$bodyB);
  
$deleted array_diff($bodyB$bodyA);
  
$output "";
  if (
$added){
      
$output .= implode("\n"$added);
  }
  
$output .= "\n";
  if (
$deleted){
    
$output .= implode("\n"$deleted);
  }
  
$output .= "\n";
  return 
$output;
}

/*
 Renvoie un score répondant à la question: quantifier le volume
 de changements que cette page a subi récemment. Pour cela, 4
 versions de la même page sont comparées:
  - La page dans son état actuel
  - La page telle qu'elle était il y a 3 jours
  - La page telle qu'elle était il y a 3 semaines
  - La page telle qu'elle était il y a 3 mois
  Les changements mesurés sont pondérés de façon décroissante
  avec l'âge.
*/
function volumeMouvement$tag ){
  global 
$wiki;
  
$req0 "SELECT body FROM ".$wiki->config["table_prefix"]."pages WHERE tag = '".mysql_escape_string($tag)."' AND latest='Y'";
  
$etat0 $wiki->LoadAll$req0 );
  
$body0 $etat0[0]["body"];

  
// ok meme si la requete echoue, car
  //      $body3jours == ""
  // <=>  la page n'existait pas encore
  // <=>  la page etait vide
  // <=>  la creation est vue comme un ajout de lignes
  
$req3jours "SELECT body FROM ".$wiki->config["table_prefix"]."pages WHERE tag = '".mysql_escape_string($tag)."' AND DATE_SUB(CURDATE(),INTERVAL 3 DAY) >= time ORDER BY time DESC LIMIT 1";
  
$etat3jours $wiki->LoadAll$req3jours );
  
$body3jours $etat3jours[0]["body"];

  
// idem que pour $body3jours
  
$req3semaines "SELECT body FROM ".$wiki->config["table_prefix"]."pages WHERE tag = '".mysql_escape_string($tag)."' AND DATE_SUB(CURDATE(),INTERVAL 21 DAY) >= time ORDER BY time DESC LIMIT 1";
  
$etat3semaines $wiki->LoadAll$req3semaines );
  
$body3semaines $etat3semaines[0]["body"];

  
// idem que pour $body3jours
  
$req3mois "SELECT body FROM ".$wiki->config["table_prefix"]."pages WHERE tag = '".mysql_escape_string($tag)."' AND DATE_SUB(CURDATE(),INTERVAL 91 DAY) >= time ORDER BY time DESC LIMIT 1";
  
$etat3mois $wiki->LoadAll$req3mois );
  
$body3mois $etat3mois[0]["body"];

  
$dif3jours pbdiff$body0$body3jours$wiki );
  
$dif3semaines pbdiff$body3jours$body3semaines$wiki ); // no overlap with $dif3jours
  
$dif3mois pbdiff$body3semaines$body3mois$wiki ); // no overlap with $dif3semaines

  
return strlen($dif3jours
    + (
strlen($dif3semaines) / 4)
    + (
strlen($dif3mois) / 16);
}

/*
 Renvoie un score "normalise" a partir d'une
 grandeur $score quelconque. Cette fonction est
 croissante en $score.
 Le $coefEtirement permet de ne pas grimper tout
 de suite au score 9, il divise simplement le $score.
 Pour la taille en octets d'une page wiki par
 exemple, un $coefEtirement de 3000 est judicieux.
 Si l'argument est positif, le score sera dans [0..9].
 Si l'argument est negatif, le score sera dans [-9..0].
 L'arctangente permet de borner le score.
*/
function normalisation($score,$coefEtirement){
  
$atanscore 20 atan((1.0*$score)/$coefEtirement) / M_PI;
  return 
intval$atanscore );
}

// On commence par rattacher toutes les pages qui ont un sommaire, a leur sommaire

$req "SELECT tag, body 
FROM "
.$this->config["table_prefix"]."pages 
WHERE latest='Y' 
AND (MATCH(tag,body) AGAINST('{{trail')
OR MATCH(tag,body) AGAINST('{{pbtrail'))"
;
$liste $this->LoadAll$req );
$sommaires = array();
$itemsSommaires = array();
foreach( 
$liste as $p ){
  if( 
ereg"trail +toc=\"([A-Za-z0-9]+)\""$p["body"], $match 
      && 
$pages$p["tag"] ]["lectureOK"]
      && 
$pages$match[1] ]["lectureOK"] ){
    
$pages$p["tag"] ]["sommaire"] = $match[1];
    
$itemsSommaire[] = $p["tag"];
    if( ! 
in_array($match[1],$sommaires) ){
      
$sommaires[] = $match[1];
    }
  }
}
//print_r( $sommaires );print "<hr/>\n";
//print_r( $itemsSommaire );print "<hr/>\n";

// Cette fonction pourrait afficher une image, ou une barre de couleur, etc.
// Arguments: des scores entre 0 et 9 (cf la fonction normalisation)
// ici elle dessinera une barre de couleur

function dessinerTagVivacite($tag,$scoreTaille$ratioMvt){
  global 
$wiki;
  
$pxLargeurBleue intval50 * ($scoreTaille * (1$ratioMvt) ));
  
$pxLargeurVerte intval50 $scoreTaille $ratioMvt );
  echo 
'<table cellpadding="0" cellspacing="0"><tr>';
  echo 
'<td>' $wiki->Link($tag) . '&nbsp;&nbsp;</td>';
  echo 
'<td bgcolor="#0000CC" width="' . (10+$pxLargeurBleue) . '">&nbsp;</td>';
  if( 
$pxLargeurVerte != ){
    echo 
'<td bgcolor="#00FF00" width="' $pxLargeurVerte '">&nbsp;</td>';
  }
  echo 
'</tr></table>';
}

// Ensuite on parcourt recursivement la table wikini_links...
// Attention, c'est un parcours en profondeur d'abord; moins joli qu'en largeur d'abord

function getlinks($from){
  global 
$wiki;
  
$req "SELECT to_tag FROM ".$wiki->config["table_prefix"]."links WHERE from_tag='" mysql_escape_string($from) . "'";
  
$resultat $wiki->LoadAll$req );
  
$links = array();
  foreach( 
$resultat as $r ){
    
$links[] = $r["to_tag"];
  }
  return 
$links;
}

// NB: on pourrait se passer de l'argument $t (nombre de tabulations)
//     mais la on s'en sert pour la couleur

function parcours_liens($tag$exclus$en_attente$pages$t){
  
array_push$exclus$tag );
  global 
$wiki;
  if( 
$pages$tag ]["lectureOK"] ){
    
$cibles array_diffgetlinks($tag), $exclus );
    
$exclus array_merge$exclus$cibles );
    
// utilisation du ratio mvt/taille
    
$taille $pages$tag ][ "taille" ];
    
$scoreTaille normalisation$taille3000 );
    
$ratioMvt = (1.0*volumeMouvement$tag )) / $taille;
    
dessinerTagVivacite($tag$scoreTaille$ratioMvt);
    print( 
'<div style=\'margin-left:20px;\'>' );
    foreach( 
$cibles as $c ){
      if( 
in_array$c$en_attente ) ){
    
$rec parcours_sommaire($c,$exclus,$en_attente,$pages$t+1);
      }else{
    
$rec parcours_liens($c,$exclus,$en_attente,$pages,  $t+1);
      }
      
$exclus $rec["exclus"];
      
$en_attente $rec["en_attente"];
    }
    print( 
"</div>\n" );
  }
  return array(
"exclus"=>$exclus,"en_attente"=>$en_attente);
}

function 
parcours_sommaire($tagSommaire$exclus$en_attente,$pages$t){
  unset( 
$en_attente[$tagSommaire] );
  global 
$wiki;
  
$jaune "#FFFF"dechex(max(0,15-$t)) ."0";
  print( 
'<br /><div style=\'margin-left:3px;background-color:' $jaune ';\'>' );
  print( 
"<b>" $wiki->Link($tagSommaire) . "</b><br/>\n" );
  print( 
'<div style=\'margin-left:20px;\'>' );
  foreach( 
$pages as $c => $page ){
    if( 
$page["sommaire"] == $tagSommaire ){
      if( 
in_array$c$en_attente ) ){
    
$rec parcours_sommaire($c,$exclus$en_attente,$pages,$t+1);
      }else{
    
$rec parcours_liens($c,$exclus,$en_attente,$pages$t+1);
      }
      
$exclus $rec["exclus"];
      
$en_attente $rec["en_attente"];
    }
  }
  print( 
"</div></div><br/>\n" );
  return array(
"exclus"=>$exclus,"en_attente"=>$en_attente);
}

parcours_liens$this->config["root_page"],
        
$itemsSommaire,
        
$sommaires,
        
$pages,
        
);

?>



Ameliorations a apporter



Si vous pensez que l'action est interessante, laissez votre nom ici, ca me motivera pour travailler dessus :)
Passé le problème de la consommation (que l'on doit en effet pouvoir améliorer), l'idée est en effet très intéressante et innovante. En revanche, je trouve le système des boules sautantes un peu pénible et pas forcément très lisible. Je verrai plutôt une barre horizontale en face de chaque page, de longueur proportionnelle à la taille des modifs et de densité de rouge proportionnelle à la distance des modifications dans le temps. -- CharlesNepote

Bon, puisque vous m'y encouragez... c'est parti j'optimise tout ça :)
Au fait, vous aurez bien un avis sur la question: qu'est-ce qui est plus rapide entre 1 requête qui renvoie 100 lignes, ou bien 3 requêtes qui renvoient 10 lignes chacune? plus généralement, comment faites-vous pour optimiser une action trop gourmande? Comment feriez-vous à ma place pour mettre en cache toutes les informations recueillies? -- RipouneT

Je croix que l'action BoingBoing est utile pour vérifier où ça bouge dans le Wiki.
L'idée serait d'attribuer:
Les XXX pages les plus actives: {{boingboing afficher="20"}}
Les pages à forte activitée: {{boingboing activité="8"}} sur une échelle de niveau de 1 à 10
ça donne {{boingboing afficher="20" activité="8"}} pour afficher les 20 premieres page ayant un niveau d'activité superieur ou égal à 8.
L'action sera trés pratique. --SloYvY

Je viens de mettre en place cette action sur Mon site. C'est une idée qui me parait vraiment intérressante J'ai eu quelques petits soucis pour la mise en place : En plus des paramètrages sur le nombres d'actions à afficher le plus gros travail semble être au niveau des requettes SQL (le nombre est impréssionant) et de l'affichage graphique qui n'est pas top !
En tout cas bravo pour le travail. C'est tres prometteur --GoubS
Il n'y a pas de commentaire sur cette page. [Afficher commentaires/formulaire]