1 | <?php
|
---|
2 | /**
|
---|
3 | * OrphansWanted Plugin: Display Orphans, Wanteds and Valid link information
|
---|
4 | * syntax ~~ORPHANSWANTED:<choice>[!<exclude list>]~~ <choice> :: orphans | wanted | valid | all
|
---|
5 | * [!<exclude list>] :: optional. prefix each with ! e.g., !wiki!comments:currentyear
|
---|
6 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
---|
7 | * @author <[email protected]>
|
---|
8 | * @author Andy Webber <dokuwiki at andywebber dot com>
|
---|
9 | * @author Federico Ariel Castagnini
|
---|
10 | * @author Cyrille37 <[email protected]>
|
---|
11 | */
|
---|
12 |
|
---|
13 | if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
|
---|
14 | if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
|
---|
15 | require_once(DOKU_PLUGIN.'syntax.php');
|
---|
16 |
|
---|
17 | require_once(DOKU_INC.'inc/search.php');
|
---|
18 |
|
---|
19 | define('DEBUG', 0);
|
---|
20 |
|
---|
21 | //-------------------------------------
|
---|
22 |
|
---|
23 | function orph_callback_search_wanted(&$data,$base,$file,$type,$lvl,$opts) {
|
---|
24 |
|
---|
25 | if($type == 'd'){
|
---|
26 | return true; // recurse all directories, but we don't store namespaces
|
---|
27 | }
|
---|
28 |
|
---|
29 | if(!preg_match("/.*\.txt$/", $file)) { // Ignore everything but TXT
|
---|
30 | return true;
|
---|
31 | }
|
---|
32 |
|
---|
33 | // search the body of the file for links
|
---|
34 | // dae mod
|
---|
35 | // orph_Check_InternalLinks(&$data,$base,$file,$type,$lvl,$opts);
|
---|
36 | orph_Check_InternalLinks($data,$base,$file,$type,$lvl,$opts);
|
---|
37 |
|
---|
38 | // get id of this file
|
---|
39 | $id = pathID($file);
|
---|
40 |
|
---|
41 | //check ACL
|
---|
42 | if(auth_quickaclcheck($id) < AUTH_READ) {
|
---|
43 | return false;
|
---|
44 | }
|
---|
45 |
|
---|
46 | // try to avoid making duplicate entries for forms and pages
|
---|
47 | $item = &$data["$id"];
|
---|
48 | if(isset($item)) {
|
---|
49 | // This item already has a member in the array
|
---|
50 | // Note that the file search found it
|
---|
51 | $item['exists'] = true;
|
---|
52 | } else {
|
---|
53 | // Create a new entry
|
---|
54 | $data["$id"]=array('exists' => true,
|
---|
55 | 'links' => 0);
|
---|
56 | }
|
---|
57 | return true;
|
---|
58 | }
|
---|
59 |
|
---|
60 | function orph_handle_link( &$data, $link )
|
---|
61 | {
|
---|
62 | if( isset($data[$link]) )
|
---|
63 | {
|
---|
64 | // This item already has a member in the array
|
---|
65 | // Note that the file search found it
|
---|
66 | $data[$link]['links'] ++ ; // count the link
|
---|
67 | } else {
|
---|
68 | // Create a new entry
|
---|
69 | $data[$link] = array(
|
---|
70 | 'exists' => false, // Only found a link, not the file
|
---|
71 | 'links' => 1
|
---|
72 | );
|
---|
73 | // echo " <!-- added link to list --> \n";
|
---|
74 | }
|
---|
75 | if (DEBUG) echo "<p>-- New count for link <b>" . $link . "</b>: " . $data[$link]['links'] . "</p>\n";
|
---|
76 | }
|
---|
77 |
|
---|
78 |
|
---|
79 | /**
|
---|
80 | * Search for internal wiki links in page $file
|
---|
81 | */
|
---|
82 | function orph_Check_InternalLinks( &$data, $base, $file, $type, $lvl, $opts )
|
---|
83 | {
|
---|
84 | global $conf;
|
---|
85 | define('LINK_PATTERN', '%\[\[([^\]|#]*)(#[^\]|]*)?\|?([^\]]*)]]%');
|
---|
86 |
|
---|
87 | if( ! preg_match("/.*\.txt$/", $file) )
|
---|
88 | {
|
---|
89 | return ;
|
---|
90 | }
|
---|
91 |
|
---|
92 | $currentID = pathID($file);
|
---|
93 | $currentNS = getNS($currentID);
|
---|
94 |
|
---|
95 | if(DEBUG) echo sprintf("<p><b>%s</b>: %s</p>\n", $file, $currentID);
|
---|
96 |
|
---|
97 | // echo " <!-- checking file: $file -->\n";
|
---|
98 | $body = @file_get_contents($conf['datadir'] . $file);
|
---|
99 |
|
---|
100 | // ignores entries in <nowiki>, %%, <code> and emails with @
|
---|
101 | foreach( array(
|
---|
102 | '/<nowiki>.*?<\/nowiki>/',
|
---|
103 | '/%%.*?%%/',
|
---|
104 | '/<code .*?>.*?<\/code>/'
|
---|
105 | )
|
---|
106 | as $ignored )
|
---|
107 | {
|
---|
108 | $body = preg_replace($ignored, '', $body);
|
---|
109 | }
|
---|
110 |
|
---|
111 | $links = array();
|
---|
112 | preg_match_all( LINK_PATTERN, $body, $links );
|
---|
113 |
|
---|
114 | foreach( $links[1] as $link )
|
---|
115 | {
|
---|
116 | if(DEBUG) echo sprintf("--- Checking %s<br />\n", $link);
|
---|
117 |
|
---|
118 | if( (0 < strlen(ltrim($link)))
|
---|
119 | and ! preg_match('/^[a-zA-Z0-9\.]+>{1}.*$/u',$link) // Interwiki
|
---|
120 | and ! preg_match('/^\\\\\\\\[\w.:?\-;,]+?\\\\/u',$link) // Windows Share
|
---|
121 | and ! preg_match('#^([a-z0-9\-\.+]+?)://#i',$link) // external link (accepts all protocols)
|
---|
122 | and ! preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link) // E-Mail (pattern above is defined in inc/mail.php)
|
---|
123 | and ! preg_match('!^#.+!',$link) // inside page link (html anchor)
|
---|
124 | ) {
|
---|
125 | $pageExists=false;
|
---|
126 | resolve_pageid($currentNS,$link,$pageExists );
|
---|
127 | if (DEBUG) echo sprintf("---- link='%s' %s ", $link, $pageExists?'EXISTS':'MISS');
|
---|
128 |
|
---|
129 | if(((strlen(ltrim($link)) > 0) // there IS an id?
|
---|
130 | and !auth_quickaclcheck($link) < AUTH_READ)) { // should be visible to user
|
---|
131 | //echo " <!-- adding $link -->\n";
|
---|
132 |
|
---|
133 | if(DEBUG) echo ' A_LINK' ;
|
---|
134 |
|
---|
135 | $link= strtolower( $link );
|
---|
136 | orph_handle_link($data, $link);
|
---|
137 | }
|
---|
138 | else
|
---|
139 | {
|
---|
140 | if(DEBUG) echo ' EMPTY_OR_FORBIDDEN' ;
|
---|
141 | }
|
---|
142 | } // link is not empty and is a local link?
|
---|
143 | else
|
---|
144 | {
|
---|
145 | if(DEBUG) echo ' NOT_INTERNAL';
|
---|
146 | }
|
---|
147 |
|
---|
148 | if(DEBUG) echo "<br />\n";
|
---|
149 | } // end of foreach link
|
---|
150 | }
|
---|
151 |
|
---|
152 |
|
---|
153 |
|
---|
154 | // --------------------
|
---|
155 |
|
---|
156 | /**
|
---|
157 | * All DokuWiki plugins to extend the parser/rendering mechanism
|
---|
158 | * need to inherit from this class
|
---|
159 | */
|
---|
160 | class syntax_plugin_orphanswanted extends DokuWiki_Syntax_Plugin {
|
---|
161 | /**
|
---|
162 | * return some info
|
---|
163 | */
|
---|
164 | function getInfo(){
|
---|
165 | return array(
|
---|
166 | 'author' => 'Doug Edmunds',
|
---|
167 | 'email' => '[email protected]',
|
---|
168 | 'date' => @file_get_contents(dirname(__FILE__) . '/VERSION'),
|
---|
169 | 'name' => 'OrphansWanted Plugin',
|
---|
170 | 'desc' => 'Find orphan pages and wanted pages .
|
---|
171 | syntax ~~ORPHANSWANTED:<choice>[!<excluded namespaces>]~~ .
|
---|
172 | <choice> :: orphans|wanted|valid|all .
|
---|
173 | <excluded namespaces> are optional, start each namespace with !' ,
|
---|
174 | 'url' => 'http://dokuwiki.org/plugin:orphanswanted',
|
---|
175 | );
|
---|
176 | }
|
---|
177 |
|
---|
178 | /**
|
---|
179 | * What kind of syntax are we?
|
---|
180 | */
|
---|
181 | function getType(){
|
---|
182 | return 'substition';
|
---|
183 | }
|
---|
184 |
|
---|
185 | /**
|
---|
186 | * What about paragraphs?
|
---|
187 | */
|
---|
188 | function getPType(){
|
---|
189 | return 'normal';
|
---|
190 | }
|
---|
191 |
|
---|
192 | /**
|
---|
193 | * Where to sort in?
|
---|
194 | */
|
---|
195 | function getSort(){
|
---|
196 | return 990; //was 990
|
---|
197 | }
|
---|
198 |
|
---|
199 |
|
---|
200 | /**
|
---|
201 | * Connect pattern to lexer
|
---|
202 | */
|
---|
203 | function connectTo($mode) {
|
---|
204 | $this->Lexer->addSpecialPattern('~~ORPHANSWANTED:[0-9a-zA-Z_:!]+~~',$mode,'plugin_orphanswanted');
|
---|
205 | }
|
---|
206 |
|
---|
207 | /**
|
---|
208 | * Handle the match
|
---|
209 | */
|
---|
210 |
|
---|
211 | function handle($match, $state, $pos, &$handler){
|
---|
212 | $match_array = array();
|
---|
213 | $match = substr($match,16,-2); //strip ~~ORPHANSWANTED: from start and ~~ from end
|
---|
214 | // Wolfgang 2007-08-29 suggests commenting out the next line
|
---|
215 | // $match = strtolower($match);
|
---|
216 | //create array, using ! as separator
|
---|
217 | $match_array = explode("!", $match);
|
---|
218 | // $match_array[0] will be orphan, wanted, valid, all, or syntax error
|
---|
219 | // if there are excluded namespaces, they will be in $match_array[1] .. [x]
|
---|
220 | // this return value appears in render() as the $data param there
|
---|
221 | return $match_array;
|
---|
222 | }
|
---|
223 |
|
---|
224 | /**
|
---|
225 | * Create output
|
---|
226 | */
|
---|
227 | function render($format, &$renderer, $data) {
|
---|
228 | global $INFO, $conf;
|
---|
229 | if($format == 'xhtml'){
|
---|
230 |
|
---|
231 | // user needs to add ~~NOCACHE~~ manually to page, to assure ACL rules are followed
|
---|
232 | // coding here is too late, it doesn't get parsed
|
---|
233 | // $renderer->doc .= "~~NOCACHE~~";
|
---|
234 |
|
---|
235 | // $data is an array
|
---|
236 | // $data[1]..[x] are excluded namespaces, $data[0] is the report type
|
---|
237 | //handle choices
|
---|
238 | switch ($data[0]){
|
---|
239 | case 'orphans':
|
---|
240 | $renderer->doc .= $this->orphan_pages($data);
|
---|
241 | break;
|
---|
242 | case 'wanted':
|
---|
243 | $renderer->doc .= $this->wanted_pages($data);
|
---|
244 | break;
|
---|
245 | case 'valid':
|
---|
246 | $renderer->doc .= $this->valid_pages($data);
|
---|
247 | break;
|
---|
248 | case 'all':
|
---|
249 | $renderer->doc .= $this->all_pages($data);
|
---|
250 | break;
|
---|
251 | default:
|
---|
252 | $renderer->doc .= "ORPHANSWANTED syntax error";
|
---|
253 | // $renderer->doc .= "syntax ~~ORPHANSWANTED:<choice>~~<optional_excluded> <choice> :: orphans|wanted|valid|all Ex: ~~ORPHANSWANTED:valid~~";
|
---|
254 | }
|
---|
255 |
|
---|
256 | return true;
|
---|
257 | }
|
---|
258 | return false;
|
---|
259 | }
|
---|
260 |
|
---|
261 |
|
---|
262 | // three choices
|
---|
263 | // $params_array used to extract excluded namespaces for report
|
---|
264 | // orphans = orph_report_table($data, true, false, $params_array);
|
---|
265 | // wanted = orph_report_table($data, false, true), $params_array;
|
---|
266 | // valid = orph_report_table($data, true, true, $params_array);
|
---|
267 |
|
---|
268 |
|
---|
269 | function orphan_pages($params_array) {
|
---|
270 | global $conf;
|
---|
271 | $result = '';
|
---|
272 | $data = array();
|
---|
273 | search($data,$conf['datadir'],'orph_callback_search_wanted',array('ns' => $ns));
|
---|
274 | $result .= $this->orph_report_table($data, true, false,$params_array);
|
---|
275 |
|
---|
276 | return $result;
|
---|
277 | }
|
---|
278 |
|
---|
279 | function wanted_pages($params_array) {
|
---|
280 | global $conf;
|
---|
281 | $result = '';
|
---|
282 | $data = array();
|
---|
283 | search($data,$conf['datadir'],'orph_callback_search_wanted',array('ns' => $ns));
|
---|
284 | $result .= $this->orph_report_table($data, false, true,$params_array);
|
---|
285 |
|
---|
286 | return $result;
|
---|
287 | }
|
---|
288 |
|
---|
289 | function valid_pages($params_array) {
|
---|
290 | global $conf;
|
---|
291 | $result = '';
|
---|
292 | $data = array();
|
---|
293 | search($data,$conf['datadir'],'orph_callback_search_wanted',array('ns' => $ns));
|
---|
294 | $result .= $this->orph_report_table($data, true, true, $params_array);
|
---|
295 |
|
---|
296 | return $result;
|
---|
297 | }
|
---|
298 |
|
---|
299 | function all_pages($params_array) {
|
---|
300 | global $conf;
|
---|
301 | $result = '';
|
---|
302 | $data = array();
|
---|
303 | search($data,$conf['datadir'],'orph_callback_search_wanted',array('ns' => $ns));
|
---|
304 |
|
---|
305 | $result .= "</p><p>Orphans</p><p>";
|
---|
306 | $result .= $this->orph_report_table($data, true, false,$params_array);
|
---|
307 | $result .= "</p><p>Wanted</p><p>";
|
---|
308 | $result .= $this->orph_report_table($data, false, true,$params_array);
|
---|
309 | $result .= "</p><p>Valid</p><p>";
|
---|
310 | $result .= $this->orph_report_table($data, true, true, $params_array);
|
---|
311 |
|
---|
312 |
|
---|
313 | return $result;
|
---|
314 | }
|
---|
315 |
|
---|
316 | function orph_report_table( $data, $page_exists, $has_links, $params_array )
|
---|
317 | {
|
---|
318 | global $conf;
|
---|
319 |
|
---|
320 | $show_heading = ($page_exists && $conf['useheading']) ? true : false ;
|
---|
321 |
|
---|
322 | //take off $params_array[0];
|
---|
323 | $exclude_array = array_slice($params_array,1);
|
---|
324 |
|
---|
325 | $count = 1;
|
---|
326 | $output = '';
|
---|
327 |
|
---|
328 | // for valid html - need to close the <p> that is feed before this
|
---|
329 | $output .= '</p>';
|
---|
330 | $output .= '<table class="inline"><tr><th> # </th><th> ID </th>'
|
---|
331 | . ($show_heading ? '<th>Title</th>' : '' )
|
---|
332 | . '<th>Links</th></tr>'
|
---|
333 | ."\n" ;
|
---|
334 |
|
---|
335 | arsort($data);
|
---|
336 |
|
---|
337 | foreach($data as $id=>$item)
|
---|
338 | {
|
---|
339 |
|
---|
340 | if( ! (($item['exists'] == $page_exists) and (($item['links'] <> 0)== $has_links)) )
|
---|
341 | {
|
---|
342 | continue ;
|
---|
343 | }
|
---|
344 |
|
---|
345 | // $id is a string, looks like this: page, namespace:page, or namespace:<subspaces>:page
|
---|
346 | $match_array = explode(":", $id);
|
---|
347 | //remove last item in array, the page identifier
|
---|
348 | $match_array = array_slice($match_array, 0, -1);
|
---|
349 | //put it back together
|
---|
350 | $page_namespace = implode (":", $match_array);
|
---|
351 | //add a trailing :
|
---|
352 | $page_namespace = $page_namespace . ':';
|
---|
353 |
|
---|
354 | //set it to show, unless blocked by exclusion list
|
---|
355 | $show_it = true;
|
---|
356 | foreach ($exclude_array as $exclude_item)
|
---|
357 | {
|
---|
358 | //add a trailing : to each $item too
|
---|
359 | $exclude_item = $exclude_item . ":";
|
---|
360 | // need === to avoid boolean false
|
---|
361 | // strpos(haystack, needle)
|
---|
362 | // if exclusion is beginning of page's namespace , block it
|
---|
363 | if (strpos($page_namespace, $exclude_item) === 0){
|
---|
364 | //there is a match, so block it
|
---|
365 | $show_it = false;
|
---|
366 | }
|
---|
367 | }
|
---|
368 |
|
---|
369 | if( $show_it )
|
---|
370 | {
|
---|
371 | $output .= "<tr><td>$count</td><td><a href=\"". wl($id)
|
---|
372 | . "\" class=\"" . ($page_exists ? "wikilink1" : "wikilink2")
|
---|
373 | . "\" onclick=\"return svchk()\" onkeypress=\"return svchk()\">"
|
---|
374 | . $id .'</a></td>'
|
---|
375 | . ($show_heading ? '<td>' . hsc(p_get_first_heading($id)) .'</td>' : '' )
|
---|
376 | . '<td>' . $item['links']
|
---|
377 | . ($has_links
|
---|
378 | ? " : <a href=\"". wl($id, 'do=backlink') ."\" class=\"wikilink1\">Show backlinks</a>"
|
---|
379 | : ''
|
---|
380 | )
|
---|
381 | . "</td></tr>\n";
|
---|
382 |
|
---|
383 | $count++;
|
---|
384 | }
|
---|
385 |
|
---|
386 | }
|
---|
387 |
|
---|
388 | $output .= "</table>\n";
|
---|
389 | //for valid html = need to reopen a <p>
|
---|
390 | $output .= '<p>';
|
---|
391 |
|
---|
392 | return $output;
|
---|
393 | }
|
---|
394 |
|
---|
395 | }
|
---|
396 |
|
---|
397 | //Setup VIM: ex: et ts=4 enc=utf-8 :
|
---|
398 | ?>
|
---|