xref: /plugin/prioritylist/syntax.php (revision 6fe0a679084f1130d38e48fbd32c561839ce660f)
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}