xref: /plugin/backlinks/syntax.php (revision e23f37e9f621287552ee5c30fce27e4ae945f6a0)
1<?php
2
3use dokuwiki\Parsing\Handler;
4use dokuwiki\Extension\SyntaxPlugin;
5use dokuwiki\File\PageResolver;
6use dokuwiki\Logger;
7use dokuwiki\Search\MetadataSearch;
8
9/**
10 * DokuWiki Syntax Plugin Backlinks.
11 *
12 * Shows a list of pages that link back to a given page.
13 *
14 * Syntax:  {{backlinks>[pagename][#filterNS|!#filterNS]}}
15 *
16 *   [pagename] - a valid wiki pagename or a . for the current page
17 *   [filterNS] - a valid,absolute namespace name, optionally prepended with ! to exclude
18 *
19 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
20 * @author  Michael Klier <chi@chimeric.de>
21 * @author  Mark C. Prins <mprins@users.sf.net>
22 */
23
24/**
25 * All DokuWiki plugins to extend the parser/rendering mechanism
26 * need to inherit from this class.
27 */
28class syntax_plugin_backlinks extends SyntaxPlugin
29{
30    /**
31     * Syntax Type.
32     *
33     * Needs to return one of the mode types defined in $PARSER_MODES in parser.php.
34     *
35     * @see DokuWiki_Syntax_Plugin::getType()
36     */
37    public function getType(): string
38    {
39        return 'substition';
40    }
41
42    /**
43     * @see DokuWiki_Syntax_Plugin::getPType()
44     */
45    public function getPType(): string
46    {
47        return 'block';
48    }
49
50    /**
51     * @see Doku_Parser_Mode::getSort()
52     */
53    public function getSort(): int
54    {
55        return 304;
56    }
57
58    /**
59     * Connect pattern to lexer.
60     *
61     * @see Doku_Parser_Mode::connectTo()
62     */
63    public function connectTo($mode): void
64    {
65        $this->Lexer->addSpecialPattern('\{\{backlinks>.+?\}\}', $mode, 'plugin_backlinks');
66    }
67
68    /**
69     * Handler to prepare matched data for the rendering process.
70     *
71     * @see DokuWiki_Syntax_Plugin::handle()
72     */
73    public function handle($match, $state, $pos, Handler $handler): array
74    {
75        // strip {{backlinks> from start and }} from end
76        $match = substr($match, 12, -2);
77
78        $includeNS = '';
79        if (str_contains($match, "#")) {
80            $includeNS = substr(strstr($match, "#"), 1);
81            $match     = strstr($match, "#", true);
82        }
83
84        return ([$match, $includeNS]);
85    }
86
87    /**
88     * Handles the actual output creation.
89     *
90     * @see DokuWiki_Syntax_Plugin::render()
91     */
92    public function render($format, Doku_Renderer $renderer, $data): bool
93    {
94        global $lang;
95        global $INFO;
96        global $ID;
97
98        $id = $ID;
99        // If it's a sidebar, get the original id.
100        if ($INFO != null) {
101            $id = $INFO['id'];
102        }
103        $match = $data[0];
104        $match = ($match == '.') ? $id : $match;
105        if (str_contains($match, ".:")) {
106            $resolver = new PageResolver($id);
107            $match = $resolver->resolveId($match);
108        }
109
110        if ($format == 'xhtml') {
111            $renderer->info['cache'] = false;
112
113            $backlinks = (new MetadataSearch())->backlinks($match);
114
115            Logger::debug("backlinks: all backlinks to: $match", $backlinks);
116
117            $renderer->doc .= '<div id="plugin__backlinks">' . "\n";
118
119            $filterNS = $data[1];
120            if ($backlinks !== [] && !empty($filterNS)) {
121                if (stripos($filterNS, "!") === 0) {
122                    $filterNS = substr($filterNS, 1);
123                    Logger::debug("backlinks: excluding all of namespace: $filterNS");
124                    $backlinks = array_filter(
125                        $backlinks,
126                        static fn($ns) => stripos($ns, $filterNS) !== 0
127                    );
128                } else {
129                    Logger::debug("backlinks: including namespace: $filterNS only");
130                    $backlinks = array_filter(
131                        $backlinks,
132                        static fn($ns) => stripos($ns, (string) $filterNS) === 0
133                    );
134                }
135            }
136
137            Logger::debug("backlinks: all backlinks to be rendered", $backlinks);
138
139            if ($backlinks !== []) {
140                $renderer->doc .= '<ul class="idx">';
141
142                foreach ($backlinks as $backlink) {
143                    $name = p_get_metadata($backlink, 'title');
144                    if (empty($name)) {
145                        $name = $backlink;
146                    }
147                    $renderer->doc .= '<li><div class="li">';
148                    $renderer->doc .= html_wikilink(':' . $backlink, $name);
149                    $renderer->doc .= '</div></li>' . "\n";
150                }
151
152                $renderer->doc .= '</ul>' . "\n";
153            } else {
154                $renderer->doc .= "<strong>Plugin Backlinks: " . $lang['nothingfound'] . "</strong>" . "\n";
155            }
156
157            $renderer->doc .= '</div>' . "\n";
158
159            return true;
160        }
161        return false;
162    }
163}
164