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