http://www.dokuwiki.org/plugin:orphanswanted
Use this plugin to find orphan pages and wanted pages.
OrphansWanted show which pages are:
Each table shows the reference count.
New version proposal : 2010-02-18, version 2.5beta.
Some changes to do it working with the “useslash” configuration option. Changes the function “orph_Check_InternalLinks()” to use more dokuwiki functions to avoid unevolutive code.
<?php /** * OrphansWanted Plugin: Display Orphans, Wanteds and Valid link information * version 2.5beta 2010-02-19 * http://www.dokuwiki.org/plugin:orphanswanted#installation * * syntax ~~ORPHANSWANTED:<choice>[!<exclude list>]~~ <choice> :: orphans | wanted | valid | all * [!<exclude list>] :: optional. prefix each with ! e.g., !wiki!comments:currentyear * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author dae@douglasedmunds.com, (changes by Cyrille37 <cyrille37@gmail.com>) * Updated by Andy Webber to include comments from DokuWiki plugin page upto 2008-11-10 */ if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'syntax.php'); require_once(DOKU_INC.'inc/search.php'); //------------------------------------- function orph_callback_search_wanted(&$data,$base,$file,$type,$lvl,$opts) { if($type == 'd'){ return true; // recurse all directories, but we don't store namespaces } if(!preg_match("/.*\.txt$/", $file)) { // Ignore everything but TXT return true; } // search the body of the file for links // dae mod // orph_Check_InternalLinks(&$data,$base,$file,$type,$lvl,$opts); orph_Check_InternalLinks($data,$base,$file,$type,$lvl,$opts); // get id of this file $id = pathID($file); //check ACL if(auth_quickaclcheck($id) < AUTH_READ) { return false; } // try to avoid making duplicate entries for forms and pages $item = &$data["$id"]; if(isset($item)) { // This item already has a member in the array // Note that the file search found it $item['exists'] = true; } else { // Create a new entry $data["$id"]=array('exists' => true, 'links' => 0); } return true; } function orph_handle_link( &$data, $link ) { if( isset($data[$link]) ) { // This item already has a member in the array // Note that the file search found it $data[$link]['links'] ++ ; // count the link // echo " <!-- added count? " . $item['links'] . " --> \n"; } else { // Create a new entry $data[$link] = array( 'exists' => false, // Only found a link, not the file 'links' => 1 ); // echo " <!-- added link to list --> \n"; } } /** * Search for internal wiki links in page $file */ function orph_Check_InternalLinks( &$data, $base, $file, $type, $lvl, $opts ) { $dbg = false ; define('LINK_PATTERN', '%\[\[([^\]|#]*)(#[^\]|]*)?\|?([^\]]*)]]%'); if( ! preg_match("/.*\.txt$/", $file) ) { return ; } if( $dbg ) echo '<p><b>'.$file.'</b></p>' ; global $conf; // echo " <!-- checking file: $file -->\n"; $body = @file_get_contents($conf['datadir'] . $file); // ignores entries in <nowiki>, %%, <code> and emails with @ foreach( array( '/<nowiki>.*<\/nowiki>/', '/%%.*%%/', '/<code .*>.*<\/code>/' ) as $ignored ) { $body = preg_replace($ignored, '', $body); } $links = array(); preg_match_all( LINK_PATTERN, $body, $links ); foreach( $links[1] as $link ) { if( $dbg ) echo $link ; if( (0 < strlen(ltrim($link))) and ! preg_match('/^[a-zA-Z0-9\.]+>{1}.*$/u',$link) // Interwiki and ! preg_match('/^\\\\\\\\[\w.:?\-;,]+?\\\\/u',$link) // Windows Share and ! preg_match('#^([a-z0-9\-\.+]+?)://#i',$link) // external link (accepts all protocols) and ! preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link) // E-Mail (pattern above is defined in inc/mail.php) and ! preg_match('!^#.+!',$link) // inside page link (html anchor) ) { $pageExists=false; resolve_pageid(false,$link,$pageExists ); //echo 'link='.$link.' '.($pageExists?'EXISTS':'MISS').'<br/>'; if(((strlen(ltrim($link)) > 0) // there IS an id? and !auth_quickaclcheck($link) < AUTH_READ)) { // should be visible to user //echo " <!-- adding $link -->\n"; if( $dbg ) echo ' A_LINK' ; $link= strtolower( $link ); orph_handle_link($data, $link); } else { if( $dbg ) echo ' EMPTY_OR_FORBIDDEN' ; } } // link is not empty and is a local link? else { if( $dbg ) echo ' NOT_INTERNAL'; } if( $dbg ) echo "<br>\n"; } // end of foreach link } // -------------------- /** * All DokuWiki plugins to extend the parser/rendering mechanism * need to inherit from this class */ class syntax_plugin_orphanswanted extends DokuWiki_Syntax_Plugin { /** * return some info */ function getInfo(){ return array( 'author' => 'Doug Edmunds', 'email' => 'dae@douglasedmunds.com', 'date' => '2010-02-18', 'name' => 'OrphansWanted Plugin ver 2.5beta (changes by Cyrille37 <cyrille37@gmail.com>)', 'desc' => 'Find orphan pages and wanted pages . syntax ~~ORPHANSWANTED:<choice>[!<excluded namespaces>]~~ . <choice> :: orphans|wanted|valid|all . <excluded namespaces> are optional, start each namespace with !' , 'url' => 'http://wiki.splitbrain.org/plugin:orphanswanted', ); } /** * What kind of syntax are we? */ function getType(){ return 'substition'; } /** * What about paragraphs? */ function getPType(){ return 'normal'; } /** * Where to sort in? */ function getSort(){ return 990; //was 990 } /** * Connect pattern to lexer */ function connectTo($mode) { $this->Lexer->addSpecialPattern('~~ORPHANSWANTED:[0-9a-zA-Z:!]+~~',$mode,'plugin_orphanswanted'); } /** * Handle the match */ function handle($match, $state, $pos, &$handler){ $match_array = array(); $match = substr($match,16,-2); //strip ~~ORPHANSWANTED: from start and ~~ from end // Wolfgang 2007-08-29 suggests commenting out the next line $match = strtolower($match); //create array, using ! as separator $match_array = explode("!", $match); // $match_array[0] will be orphan, wanted, valid, all, or syntax error // if there are excluded namespaces, they will be in $match_array[1] .. [x] // this return value appears in render() as the $data param there return $match_array; } /** * Create output */ function render($format, &$renderer, $data) { global $INFO, $conf; if($format == 'xhtml'){ // user needs to add ~~NOCACHE~~ manually to page, to assure ACL rules are followed // coding here is too late, it doesn't get parsed // $renderer->doc .= "~~NOCACHE~~"; // $data is an array // $data[1]..[x] are excluded namespaces, $data[0] is the report type //handle choices switch ($data[0]){ case 'orphans': $renderer->doc .= $this->orphan_pages($data); break; case 'wanted': $renderer->doc .= $this->wanted_pages($data); break; case 'valid': $renderer->doc .= $this->valid_pages($data); break; case 'all': $renderer->doc .= $this->all_pages($data); break; default: $renderer->doc .= "ORPHANSWANTED syntax error"; // $renderer->doc .= "syntax ~~ORPHANSWANTED:<choice>~~<optional_excluded> <choice> :: orphans|wanted|valid|all Ex: ~~ORPHANSWANTED:valid~~"; } return true; } return false; } // three choices // $params_array used to extract excluded namespaces for report // orphans = orph_report_table($data, true, false, $params_array); // wanted = orph_report_table($data, false, true), $params_array; // valid = orph_report_table($data, true, true, $params_array); function orphan_pages($params_array) { global $conf; $result = ''; $data = array(); search($data,$conf['datadir'],'orph_callback_search_wanted',array('ns' => $ns)); $result .= $this->orph_report_table($data, true, false,$params_array); return $result; } function wanted_pages($params_array) { global $conf; $result = ''; $data = array(); search($data,$conf['datadir'],'orph_callback_search_wanted',array('ns' => $ns)); $result .= $this->orph_report_table($data, false, true,$params_array); return $result; } function valid_pages($params_array) { global $conf; $result = ''; $data = array(); search($data,$conf['datadir'],'orph_callback_search_wanted',array('ns' => $ns)); $result .= $this->orph_report_table($data, true, true, $params_array); return $result; } function all_pages($params_array) { global $conf; $result = ''; $data = array(); search($data,$conf['datadir'],'orph_callback_search_wanted',array('ns' => $ns)); $result .= "</p><p>Orphans</p><p>"; $result .= $this->orph_report_table($data, true, false,$params_array); $result .= "</p><p>Wanted</p><p>"; $result .= $this->orph_report_table($data, false, true,$params_array); $result .= "</p><p>Valid</p><p>"; $result .= $this->orph_report_table($data, true, true, $params_array); return $result; } function orph_report_table( $data, $page_exists, $has_links, $params_array ) { global $conf; $show_heading = ($page_exists && $conf['useheading']) ? true : false ; //take off $params_array[0]; $exclude_array = array_slice($params_array,1); $count = 1; $output = ''; // for valid html - need to close the <p> that is feed before this $output .= '</p>'; $output .= '<table class="inline"><tr><th> # </th><th> ID </th>' . ($show_heading ? '<th>Title</th>' : '' ) . '<th>Links</th></tr>' ."\n" ; arsort($data); foreach($data as $id=>$item) { if( ! (($item['exists'] == $page_exists) and (($item['links'] <> 0)== $has_links)) ) { continue ; } // $id is a string, looks like this: page, namespace:page, or namespace:<subspaces>:page $match_array = explode(":", $id); //remove last item in array, the page identifier $match_array = array_slice($match_array, 0, -1); //put it back together $page_namespace = implode (":", $match_array); //add a trailing : $page_namespace = $page_namespace . ':'; //set it to show, unless blocked by exclusion list $show_it = true; foreach ($exclude_array as $exclude_item) { //add a trailing : to each $item too $exclude_item = $exclude_item . ":"; // need === to avoid boolean false // strpos(haystack, needle) // if exclusion is beginning of page's namespace , block it if (strpos($page_namespace, $exclude_item) === 0){ //there is a match, so block it $show_it = false; } } if( $show_it ) { $output .= "<tr><td>$count</td><td><a href=\"". wl($id) . "\" class=\"" . ($page_exists ? "wikilink1" : "wikilink2") . "\" onclick=\"return svchk()\" onkeypress=\"return svchk()\">" . $id .'</a></td>' . ($show_heading ? '<td>' . hsc(p_get_first_heading($id)) .'</td>' : '' ) . '<td>' . $item['links'] . ($has_links ? " : <a href=\"". wl($id) . "&do=backlink\" class=\"wikilink1\">Show backlinks</a>" : '' ) . "</td></tr>\n"; $count++; } } $output .= "</table>\n"; //for valid html = need to reopen a <p> $output .= '<p>'; return $output; } } ?>