1<?php 2/** 3 * RevisionsDue Plugin: Find pages that should be revised as specified by the <revision_frequency>YYY</revision_frequency> xml. 4 * syntax ~~REVISIONS:<choice>~~ <choice> :: all 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author <Stephan@SparklingSoftware.com.au> 7 * @author Stephan Dekker <Stephan@SparklingSoftware.com.au> 8 */ 9 10if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 11if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 12require_once(DOKU_PLUGIN.'syntax.php'); 13 14require_once(DOKU_INC.'inc/search.php'); 15 16define('DEBUG', 0); 17 18function revision_callback_search_wanted(&$data,$base,$file,$type,$lvl,$opts) { 19 global $conf; 20 21 if($type == 'd'){ 22 return true; // recurse all directories, but we don't store namespaces 23 } 24 25 if(!preg_match("/.*\.txt$/", $file)) { // Ignore everything but TXT 26 return true; 27 } 28 29 // get id of this file 30 $id = pathID($file); 31 32 //check ACL 33 if(auth_quickaclcheck($id) < AUTH_READ) { 34 return false; 35 } 36 37 $revision_frequency = get_revision_frequency($file); 38 39 // try to avoid making duplicate entries for forms and pages 40 $item = &$data["$id"]; 41 if(! isset($item)) { 42 // Create a new entry 43 $filename = $conf['datadir'].$file; 44 $last_modified = filemtime($filename); 45 46 $revision_date = $last_modified + (intval($revision_frequency) * 86400); 47 if ($revision_date < time()) { 48 $data["$id"]=array('revision' => $last_modified, 49 'frequency' => $revision_frequency, 50 'revision_date' => $revision_date ); 51 } 52 else 53 { 54 // The item is actually already added to the array! We need to remove it if we don't need it. 55 unset($data["$id"]); 56 } 57 } 58 59 return true; 60} 61 62function revision_string($revision) { 63 64 $result = date("Y-m-d", $revision); 65 return $result; 66} 67 68function get_revision_frequency($file) { 69 global $conf; 70 71 $filename = $conf['datadir'].$file; 72 $body = @file_get_contents($filename); 73 $pattern = '/<revision_frequency>\-?\d+<\/revision_frequency>/i'; 74 $count = preg_match($pattern, $body, $matches); 75 $result = htmlspecialchars($matches[0]); 76 $result = str_replace(htmlspecialchars('<revision_frequency>'), "", $result); 77 $result = str_replace(htmlspecialchars('</revision_frequency>'), "", $result); 78 79 return $result; 80} 81 82function date_compare($a, $b) { 83 if ($a['revision_date'] == $b['revision_date']) { 84 return 0; 85 } 86 return ($a['revision_date'] < $b['revision_date']) ? -1 : 1; 87} 88 89 90/** 91 * All DokuWiki plugins to extend the parser/rendering mechanism 92 * need to inherit from this class 93 */ 94class syntax_plugin_revisionsdue extends DokuWiki_Syntax_Plugin { 95 /** 96 * return some info 97 */ 98 function getInfo(){ 99 return array( 100 'author' => 'Stephan Dekker', 101 'email' => 'Stephan@SparklingSoftware.com.au', 102 'date' => @file_get_contents(dirname(__FILE__) . '/VERSION'), 103 'name' => 'RevisionsDue Plugin', 104 'desc' => 'Find pages that should be revised as specified by the <revision_frequency>YYY</revision_frequency> xml. 105 syntax ~~REVISIONS:<choice>~~ . 106 <choice> :: all', 107 'url' => 'http://dokuwiki.org/plugin:revisionsdue', 108 ); 109 } 110 111 /** 112 * What kind of syntax are we? 113 */ 114 function getType(){ 115 return 'substition'; 116 } 117 118 /** 119 * What about paragraphs? 120 */ 121 function getPType(){ 122 return 'normal'; 123 } 124 125 /** 126 * Where to sort in? 127 */ 128 function getSort(){ 129 return 990; //was 990 130 } 131 132 133 /** 134 * Connect pattern to lexer 135 */ 136 function connectTo($mode) { 137 $this->Lexer->addSpecialPattern('~~REVISIONS:[0-9a-zA-Z_:!]+~~',$mode,'plugin_revisionsdue'); 138 } 139 140 /** 141 * Handle the match 142 */ 143 144 function handle($match, $state, $pos, &$handler){ 145 $match_array = array(); 146 $match = substr($match,12,-2); //strip ~~REVISIONS: from start and ~~ from end 147 // Wolfgang 2007-08-29 suggests commenting out the next line 148 // $match = strtolower($match); 149 //create array, using ! as separator 150 $match_array = explode("!", $match); 151 // $match_array[0] will be all, or syntax error 152 // this return value appears in render() as the $data param there 153 return $match_array; 154 } 155 156 /** 157 * Create output 158 */ 159 function render($format, &$renderer, $data) { 160 global $INFO, $conf; 161 162 if($format == 'xhtml'){ 163 164 // user needs to add ~~NOCACHE~~ manually to page, to assure ACL rules are followed 165 // coding here is too late, it doesn't get parsed 166 // $renderer->doc .= "~~NOCACHE~~"; 167 168 // $data is an array 169 // $data[1]..[x] are excluded namespaces, $data[0] is the report type 170 //handle choices 171 switch ($data[0]){ 172 case 'all': 173 $renderer->doc .= $this->all_pages($data); 174 break; 175 case 'revisions': 176 $renderer->doc .= $this->all_pages($data, false); 177 break; 178 default: 179 $renderer->doc .= "RevisionsDue syntax error"; 180 // $renderer->doc .= "syntax ~~REVISIONS:<choice>~~<choice> :: all Example: ~~REVISIONS:all~~"; 181 } 182 183 return true; 184 } 185 return false; 186 } 187 188 function all_pages($params_array, $mandatory_revisions = true ) { 189 global $conf; 190 $result = ''; 191 $data = array(); 192 search($data,$conf['datadir'],'revision_callback_search_wanted',array('ns' => $ns)); 193 194 $result .= $this->revision_report_table($data, $mandatory_revisions); 195 196 return $result; 197 } 198 199 function revision_report_table( $data, $mandatory_revisions = true ) 200 { 201 global $conf; 202 203 $count = 1; 204 $output = ''; 205 206 // for valid html - need to close the <p> that is feed before this 207 $output .= '</p>'; 208 $output .= '<table class="inline"><tr><th> # </th><th>Title</th><th>Last Revision</th><th>Frequency</th><th>Next Revision Date</th></tr>'."\n" ; 209 210 uasort($data, 'date_compare'); 211 212 foreach($data as $id=>$item) 213 { 214 if( $item['revision'] === '' ) { 215 continue ; 216 } 217 218 if ($mandatory_revisions === false and ($item['frequency'] === '' or !isset($item['frequency']))) { 219 continue ; 220 } 221 222 // $id is a string, looks like this: page, namespace:page, or namespace:<subspaces>:page 223 $match_array = explode(":", $id); 224 //remove last item in array, the page identifier 225 $match_array = array_slice($match_array, 0, -1); 226 //put it back together 227 $page_namespace = implode (":", $match_array); 228 //add a trailing : 229 $page_namespace = $page_namespace . ':'; 230 231 //set it to show, unless blocked by exclusion list 232 $show_it = true; 233 foreach ($exclude_array as $exclude_item) 234 { 235 //add a trailing : to each $item too 236 $exclude_item = $exclude_item . ":"; 237 // need === to avoid boolean false 238 // strpos(haystack, needle) 239 // if exclusion is beginning of page's namespace , block it 240 if (strpos($page_namespace, $exclude_item) === 0){ 241 //there is a match, so block it 242 $show_it = false; 243 } 244 } 245 246 if( $show_it ) 247 { 248 $revision = $item['revision']; 249 $frequency = $item['frequency']; 250 $revision_date = $item['revision_date']; 251 252// msg('Creating teable for:'.$id.' was modified: '.date ("F d Y H:i:s.", $revision)); 253 254 $output .= "<tr><td>$count</td><td><a href=\"". wl($id) 255 . "\" class=\"" . "wikilink1" 256 . "\" onclick=\"return svchk()\" onkeypress=\"return svchk()\">" 257 . $id .'</a></td><td>'.revision_string($revision)."</td><td>".$frequency."</td><td>".revision_string($revision_date)."</td></tr>\n"; 258 259 $count++; 260 } 261 262 } 263 264 $output .= "</table>\n"; 265 //for valid html = need to reopen a <p> 266 $output .= '<p>'; 267 268 return $output; 269 } 270 271 272} 273 274 275//Setup VIM: ex: et ts=4 enc=utf-8 : 276?> 277