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