1*6fe0a679SZpartakov<?php 2*6fe0a679SZpartakov 3*6fe0a679SZpartakovif(!defined('DOKU_INC')) die(); 4*6fe0a679SZpartakov 5*6fe0a679SZpartakovclass syntax_plugin_prioritylist extends DokuWiki_Syntax_Plugin { 6*6fe0a679SZpartakov 7*6fe0a679SZpartakov function getType() { return 'substition'; } 8*6fe0a679SZpartakov function getPType() { return 'block'; } 9*6fe0a679SZpartakov function getSort() { return 150; } 10*6fe0a679SZpartakov 11*6fe0a679SZpartakov function connectTo($mode) { 12*6fe0a679SZpartakov $this->Lexer->addSpecialPattern('<prioritylist.*?>', $mode, 'plugin_prioritylist'); 13*6fe0a679SZpartakov } 14*6fe0a679SZpartakov 15*6fe0a679SZpartakov function handle($match, $state, $pos, Doku_Handler $handler) { 16*6fe0a679SZpartakov $match = substr($match, 13, -2); 17*6fe0a679SZpartakov $args = explode(' ', $match); 18*6fe0a679SZpartakov $data = array( 19*6fe0a679SZpartakov 'ns' => 'billets', 20*6fe0a679SZpartakov 'limit' => 0 21*6fe0a679SZpartakov ); 22*6fe0a679SZpartakov 23*6fe0a679SZpartakov foreach($args as $arg) { 24*6fe0a679SZpartakov $arg = trim($arg); 25*6fe0a679SZpartakov if(empty($arg)) continue; 26*6fe0a679SZpartakov if(strpos($arg, '=') !== false) { 27*6fe0a679SZpartakov list($key, $val) = explode('=', $arg, 2); 28*6fe0a679SZpartakov $key = trim($key); 29*6fe0a679SZpartakov $val = trim($val); 30*6fe0a679SZpartakov if($key == 'ns') $data['ns'] = $val; 31*6fe0a679SZpartakov if($key == 'limit') $data['limit'] = (int)$val; 32*6fe0a679SZpartakov } 33*6fe0a679SZpartakov } 34*6fe0a679SZpartakov return $data; 35*6fe0a679SZpartakov } 36*6fe0a679SZpartakov 37*6fe0a679SZpartakov function render($format, Doku_Renderer $renderer, $data) { 38*6fe0a679SZpartakov if ($format != 'xhtml') return false; 39*6fe0a679SZpartakov 40*6fe0a679SZpartakov // 1. Récupération DYNAMIQUE du répertoire de données 41*6fe0a679SZpartakov global $conf; 42*6fe0a679SZpartakov $savedir = $conf['savedir']; 43*6fe0a679SZpartakov $savedir = rtrim(ltrim($savedir, './'), '/'); 44*6fe0a679SZpartakov $dataDir = DOKU_INC . $savedir . '/pages'; 45*6fe0a679SZpartakov 46*6fe0a679SZpartakov $ns = rtrim($data['ns'], ':'); 47*6fe0a679SZpartakov $limit = $data['limit']; 48*6fe0a679SZpartakov 49*6fe0a679SZpartakov $pages = array(); 50*6fe0a679SZpartakov $scanPath = $dataDir . '/' . str_replace(':', '/', $ns); 51*6fe0a679SZpartakov 52*6fe0a679SZpartakov if (!is_dir($scanPath)) { 53*6fe0a679SZpartakov $renderer->doc .= '<div style="border:1px solid red; padding:10px; background:#ffe0e0;">'; 54*6fe0a679SZpartakov $renderer->doc .= '<strong>Erreur :</strong> Le dossier <code>' . hsc($scanPath) . '</code> n\'existe pas.<br>'; 55*6fe0a679SZpartakov $renderer->doc .= 'Chemin détecté : ' . hsc($savedir) . '/pages/' . hsc($ns); 56*6fe0a679SZpartakov $renderer->doc .= '</div>'; 57*6fe0a679SZpartakov return true; 58*6fe0a679SZpartakov } 59*6fe0a679SZpartakov 60*6fe0a679SZpartakov // 2. Récupération des pages (Native + Fallback) 61*6fe0a679SZpartakov search($pages, $dataDir, 'search_allpages', array('ns' => $ns), $ns); 62*6fe0a679SZpartakov if (empty($pages)) { 63*6fe0a679SZpartakov $this->scanDirectoryRecursive($scanPath, $ns, $pages); 64*6fe0a679SZpartakov } 65*6fe0a679SZpartakov 66*6fe0a679SZpartakov $filteredPages = array(); 67*6fe0a679SZpartakov $totalCount = count($pages); 68*6fe0a679SZpartakov 69*6fe0a679SZpartakov foreach ($pages as $page) { 70*6fe0a679SZpartakov $id = $page['id']; 71*6fe0a679SZpartakov $file = wikiFN($id); 72*6fe0a679SZpartakov 73*6fe0a679SZpartakov if (!file_exists($file)) continue; 74*6fe0a679SZpartakov 75*6fe0a679SZpartakov // Lecture élargie (8ko) pour capturer le titre ET les tags potentiellement plus bas 76*6fe0a679SZpartakov $content = file_get_contents($file, false, null, 0, 8192); 77*6fe0a679SZpartakov if ($content === false) continue; 78*6fe0a679SZpartakov 79*6fe0a679SZpartakov // A. Vérification du titre (+, ++, +++) 80*6fe0a679SZpartakov if (!preg_match('/^======\s*(\+{1,3}.*?)\s*======/m', $content, $titleMatches)) { 81*6fe0a679SZpartakov continue; 82*6fe0a679SZpartakov } 83*6fe0a679SZpartakov $title = trim($titleMatches[1]); 84*6fe0a679SZpartakov 85*6fe0a679SZpartakov // B. Extraction des Tags 86*6fe0a679SZpartakov $tags = $this->extractTags($id, $content); 87*6fe0a679SZpartakov 88*6fe0a679SZpartakov $filteredPages[] = array( 89*6fe0a679SZpartakov 'id' => $id, 90*6fe0a679SZpartakov 'title' => $title, 91*6fe0a679SZpartakov 'tags' => $tags, 92*6fe0a679SZpartakov 'mtime' => $page['mtime'] 93*6fe0a679SZpartakov ); 94*6fe0a679SZpartakov } 95*6fe0a679SZpartakov 96*6fe0a679SZpartakov // Tri antéchronologique 97*6fe0a679SZpartakov usort($filteredPages, function($a, $b) { 98*6fe0a679SZpartakov return $b['mtime'] - $a['mtime']; 99*6fe0a679SZpartakov }); 100*6fe0a679SZpartakov 101*6fe0a679SZpartakov if ($limit > 0) { 102*6fe0a679SZpartakov $filteredPages = array_slice($filteredPages, 0, $limit); 103*6fe0a679SZpartakov } 104*6fe0a679SZpartakov 105*6fe0a679SZpartakov // Affichage 106*6fe0a679SZpartakov if (empty($filteredPages)) { 107*6fe0a679SZpartakov $renderer->doc .= '<p>Aucun billet prioritaire trouvé dans <code>' . hsc($ns) . '</code>.</p>'; 108*6fe0a679SZpartakov $renderer->doc .= '<p><small>Pages scannées : ' . $totalCount . '</small></p>'; 109*6fe0a679SZpartakov return true; 110*6fe0a679SZpartakov } 111*6fe0a679SZpartakov 112*6fe0a679SZpartakov $renderer->doc .= '<div class="plugin_prioritylist">'; 113*6fe0a679SZpartakov $renderer->doc .= '<h3>' . count($filteredPages) . ' billet(s) prioritaire(s)</h3>'; 114*6fe0a679SZpartakov $renderer->doc .= '<ul>'; 115*6fe0a679SZpartakov 116*6fe0a679SZpartakov foreach ($filteredPages as $p) { 117*6fe0a679SZpartakov $date = date('d.m.Y', $p['mtime']); 118*6fe0a679SZpartakov $link = wl($p['id']); 119*6fe0a679SZpartakov $titleHtml = hsc($p['title']); 120*6fe0a679SZpartakov 121*6fe0a679SZpartakov // Formatage des tags 122*6fe0a679SZpartakov $tagsHtml = ''; 123*6fe0a679SZpartakov if (!empty($p['tags'])) { 124*6fe0a679SZpartakov $tagsList = array_map('hsc', $p['tags']); 125*6fe0a679SZpartakov $tagsHtml = ' <span style="color:#555; font-size:0.85em;">(' . implode('; ', $tagsList) . ')</span>'; 126*6fe0a679SZpartakov } 127*6fe0a679SZpartakov 128*6fe0a679SZpartakov $renderer->doc .= '<li class="priority-item" style="margin-bottom: 0.5em; list-style-type: none;">'; 129*6fe0a679SZpartakov #$renderer->doc .= '<span class="priority-date" style="color: #666; font-size: 0.9em;">[' . $date . ']</span> '; 130*6fe0a679SZpartakov $renderer->doc .= '<a href="' . $link . '" class="wikilink" style="font-weight:bold;">' . $titleHtml . '</a>'; 131*6fe0a679SZpartakov $renderer->doc .= $tagsHtml; 132*6fe0a679SZpartakov $renderer->doc .= '</li>'; 133*6fe0a679SZpartakov } 134*6fe0a679SZpartakov 135*6fe0a679SZpartakov $renderer->doc .= '</ul>'; 136*6fe0a679SZpartakov $renderer->doc .= '</div>'; 137*6fe0a679SZpartakov 138*6fe0a679SZpartakov return true; 139*6fe0a679SZpartakov } 140*6fe0a679SZpartakov 141*6fe0a679SZpartakov /** 142*6fe0a679SZpartakov * Extrait les tags du contenu ou via l'API metadata de DokuWiki 143*6fe0a679SZpartakov */ 144*6fe0a679SZpartakov function extractTags($id, $content) { 145*6fe0a679SZpartakov // Méthode 1 : Essayer d'utiliser l'API metadata de DokuWiki (si le plugin Tag est installé et actif) 146*6fe0a679SZpartakov // On utilise ft_getMetaTags qui est la fonction standard pour récupérer les tags parsed. 147*6fe0a679SZpartakov if (function_exists('ft_getMetaTags')) { 148*6fe0a679SZpartakov $metaTags = ft_getMetaTags($id); 149*6fe0a679SZpartakov if (!empty($metaTags)) { 150*6fe0a679SZpartakov // ft_getMetaTags retourne souvent un tableau associatif [tag => count] ou similaire selon la version 151*6fe0a679SZpartakov // On veut juste les noms des tags 152*6fe0a679SZpartakov $tags = array(); 153*6fe0a679SZpartakov foreach ($metaTags as $tag => $count) { 154*6fe0a679SZpartakov // Si le tableau est [tag => count], la clé est le nom. 155*6fe0a679SZpartakov // Si c'est [0 => ['tag' => name, 'count' => 1]], on adapte. 156*6fe0a679SZpartakov if (is_int($tag)) { 157*6fe0a679SZpartakov // Cas tableau indexé 158*6fe0a679SZpartakov if (isset($count['tag'])) $tags[] = $count['tag']; 159*6fe0a679SZpartakov } else { 160*6fe0a679SZpartakov // Cas tableau associatif direct 161*6fe0a679SZpartakov $tags[] = $tag; 162*6fe0a679SZpartakov } 163*6fe0a679SZpartakov } 164*6fe0a679SZpartakov if (!empty($tags)) return $tags; 165*6fe0a679SZpartakov } 166*6fe0a679SZpartakov } 167*6fe0a679SZpartakov 168*6fe0a679SZpartakov // Méthode 2 : Fallback par Regex sur le contenu brut si l'API ne donne rien 169*6fe0a679SZpartakov // Cherche {{tag>tag1 tag2}} ou {{tag>tag1}} 170*6fe0a679SZpartakov $tags = array(); 171*6fe0a679SZpartakov if (preg_match_all('/\{\{tag>([^}]+)\}\}/i', $content, $matches)) { 172*6fe0a679SZpartakov foreach ($matches[1] as $tagBlock) { 173*6fe0a679SZpartakov // Les tags peuvent être séparés par des espaces dans le bloc {{tag>...}} 174*6fe0a679SZpartakov $parts = preg_split('/\s+/', trim($tagBlock)); 175*6fe0a679SZpartakov foreach ($parts as $part) { 176*6fe0a679SZpartakov $part = trim($part); 177*6fe0a679SZpartakov if (!empty($part) && !in_array($part, $tags)) { 178*6fe0a679SZpartakov $tags[] = $part; 179*6fe0a679SZpartakov } 180*6fe0a679SZpartakov } 181*6fe0a679SZpartakov } 182*6fe0a679SZpartakov } 183*6fe0a679SZpartakov return $tags; 184*6fe0a679SZpartakov } 185*6fe0a679SZpartakov 186*6fe0a679SZpartakov // Scanner manuel récursif 187*6fe0a679SZpartakov function scanDirectoryRecursive($path, $nsPrefix, &$result) { 188*6fe0a679SZpartakov if (!is_dir($path)) return; 189*6fe0a679SZpartakov $files = array_diff(scandir($path), array('.', '..', '_media', '_cache')); 190*6fe0a679SZpartakov 191*6fe0a679SZpartakov foreach ($files as $file) { 192*6fe0a679SZpartakov $fullPath = $path . '/' . $file; 193*6fe0a679SZpartakov if (is_dir($fullPath)) { 194*6fe0a679SZpartakov $newNs = $nsPrefix ? $nsPrefix . ':' . $file : $file; 195*6fe0a679SZpartakov $this->scanDirectoryRecursive($fullPath, $newNs, $result); 196*6fe0a679SZpartakov } elseif (substr($file, -4) == '.txt') { 197*6fe0a679SZpartakov $pageId = $nsPrefix ? $nsPrefix . ':' . substr($file, 0, -4) : substr($file, 0, -4); 198*6fe0a679SZpartakov $result[] = array( 199*6fe0a679SZpartakov 'id' => $pageId, 200*6fe0a679SZpartakov 'mtime' => filemtime($fullPath) 201*6fe0a679SZpartakov ); 202*6fe0a679SZpartakov } 203*6fe0a679SZpartakov } 204*6fe0a679SZpartakov } 205*6fe0a679SZpartakov}