1<?php 2/** 3 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 4 * @author Otto Vainio <oiv-plugins@valjakko.net> 5 * Ideas "borrowed" from Esther Brunners tag plugin. 6 * Version 8.3.2007 Fixed replace patter to use back reference to keep case of replaced text 7 * Version 11.10.2011 Quote regular expression characters 8 */ 9 10// must be run within Dokuwiki 11if (!defined('DOKU_INC')) die(); 12 13class helper_plugin_autolink2 extends DokuWiki_Plugin { 14 15 var $idx_dir = ''; // directory for index files 16 var $page_idx = array(); // array of existing pages 17 var $autolink_idx = array(); // array of anchors and index in which pages they are found 18 19 /** 20 * Constructor gets default preferences and language strings 21 */ 22 function helper_plugin_autolink2(){ 23 global $ID, $conf; 24 25 // determine where index files are saved 26 if (@file_exists($conf['indexdir'].'/page.idx')){ // new word length based index 27 $this->idx_dir = $conf['indexdir']; 28 $this->page_idx = @file($this->idx_dir.'/page.idx'); 29 if (!@file_exists($this->idx_dir.'/autolink.idx')) $this->_importOldAutolinkIndex('index'); 30 } else { // old index 31 $this->idx_dir = $conf['cachedir']; 32 $this->page_idx = @file($this->idx_dir.'/page.idx'); 33 if (!@file_exists($this->idx_dir.'/autolink.idx')) $this->_importOldAutolinkIndex('cache'); 34 } 35 36 // load page and tag index 37 $autolink_index = @file($this->idx_dir.'/autolink.idx'); 38 usort($autolink_index,array("helper_plugin_autolink2","lensort")); 39 40 if (is_array($autolink_index)){ 41 foreach ($autolink_index as $idx_line){ 42 list($key, $value) = explode("\t", $idx_line, 2); 43 if ($value) { 44 $this->autolink_idx[$key]=trim($value); 45 } 46 } 47 } 48 } 49 50 function getInfo() { 51 return confToHash(dirname(__FILE__).'/plugin.info.txt'); 52 } 53 54 function getMethods(){ 55 $result = array(); 56 $result[] = array( 57 'name' => 'getAnchors', 58 'desc' => 'returns pattern and replace arrays for replace', 59 'return' => array('{pattern,replace}' => 'array'), 60 ); 61 return $result; 62 } 63 64 65 function getAnchors() { 66 $result = array(); // array of line numbers in the page index 67// $result = array_merge($result, $this->autolink_idx); 68 $result = $this->autolink_idx; 69 $res=$this->_numToID($result); 70 if (is_array($res)){ 71 $l=0; 72 $sr="-anchorlink-"; 73 $er="-knilrohcna-"; 74 $sp="-anchorlink-"; 75 $ep="-knilrohcna-"; 76 $hasCustom=false; 77 $customStart=""; 78 $customEnd=""; 79 if ($this->getConf('customfilter_start') && $this->getConf('customfilter_end')) { 80 $customStart=$this->getConf('customfilter_start'); 81 $customEnd=$this->getConf('customfilter_end'); 82 $hasCustom=true; 83 } 84 foreach ($res as $anchor => $page){ 85 // Mark everything close to possible as a potential autolink anchor 86 $pattern[$l]="/(?<= |\\n|\\t|\|)(".$anchor.")(?=( |,|\.|:|\\n|\\t|\|))/msi"; 87 // $replace[$l++]=$sr.$anchor.$er; 88 $replace[$l++]=$sr."\\1".$er; 89 // Remove anchor from headings 90 $pattern[$l]="/(={1,6}.*?)".$sp."(".$anchor.")".$ep."(.*?={1,6})/i"; 91 // $replace[$l++]="$1".$anchor."$3"; 92 $replace[$l++]="$1$2$3"; 93 // Remove anchor from media (and some plugin) refs = {{something}} 94 $pattern[$l]="/(\{\{.*?)".$sp."(".$anchor.")".$ep."(.*?\}\})/i"; 95 $replace[$l++]="$1$2$3"; 96 // Remove anchor from links refs = [[something]] 97 $pattern[$l]="/(\[\[.*)".$sp."(".$anchor.")".$ep."(.*\]\])/Ui"; 98 $replace[$l++]="$1$2$3"; 99 // Remove anchor from links refs = <something> 100 $pattern[$l]="/(\<.*)".$sp."(".$anchor.")".$ep."(.*\>)/Ui"; 101 $replace[$l++]="$1$2$3"; 102 // Remove custom pattern links. 103 if ($hasCustom==true) { 104 $pattern[$l]=$customStart.$sp."(".$anchor.")".$ep.$customEnd; 105 $replace[$l++]="$1$2$3"; 106 } 107 108 // Finally change all that's left to links 109 $pattern[$l]="/".$sp."(".$anchor.")".$ep."/i"; 110 // $replace[$l++]="[[:".$page."|".$anchor."]]"; 111 $replace[$l++]="[[:".$page."|$1]]"; 112 113 } 114 if (is_array($pattern)) ksort($pattern); 115 if (is_array($replace)) ksort($replace); 116 } 117 118 // now convert to page IDs and return 119 return array($pattern,$replace); 120 } 121 122 function lensort($a,$b){ 123 return strlen($b)-strlen($a); 124 } 125 126 /** 127 * Update Autolink index 128 */ 129 function _updateAutolinkIndex($id, $autolinks){ 130 global $ID, $INFO; 131 if (!is_array($autolinks) || empty($autolinks)) return false; 132 usort($autolinks,array("helper_plugin_autolink2","lensort")); 133 $changed = false; 134 // get page id (this is the linenumber in page.idx) 135 $pid = array_search("$id\n", $this->page_idx); 136 if (!is_int($pid)){ 137 $this->page_idx[] = "$id\n"; 138 $pid = count($this->page_idx) - 1; 139 // page was new - write back 140 $this->_saveIndex('page'); 141 } 142 143 // clean array first 144 $c = count($autolinks); 145 for ($i = 0; $i <= $c; $i++){ 146 $autolinks[$i] = utf8_strtolower(preg_quote($autolinks[$i], '/')); 147 } 148 // clear no longer used autolinks 149 if ($ID == $id){ 150 $oldautolinks = $INFO['meta']['anchors']; 151 $oldautolinks = $this->getoldautolinks($pid); 152 if (!is_array($oldautolinks)) $oldautolinks = explode(' ', $oldautolinks); 153 foreach ($oldautolinks as $oldtag){ 154 if (!$oldtag) continue; // skip empty autolinks 155 $oldtag = utf8_strtolower($oldtag); 156 if (in_array($oldtag, $autolinks)) continue; // tag is still there 157 $this->autolink_idx[$oldtag]=""; 158 $changed = true; 159 } 160 } 161 162 // fill tag in 163 foreach ($autolinks as $autolink){ 164 if (!$autolink) continue; // skip empty autolinks 165 if ($this->autolink_idx[$autolink]!=$pid){ 166 $this->autolink_idx[$autolink] = $pid; 167 $changed = true; 168 } 169 } 170 171 // save tag index 172 if ($changed) return $this->_saveIndex('autolink'); 173 else return true; 174 } 175 176 /** 177 * Remove Autolink index 178 */ 179 function _removeAutolinkIndex($id){ 180 global $ID, $INFO; 181 182 // get page id (this is the linenumber in page.idx) 183 $pid = array_search("$id\n", $this->page_idx); 184 if (!is_int($pid)){ 185 return; 186 } 187 188 // clear no longer used autolinks 189 if ($ID == $id){ 190 $oldautolinks = $INFO['meta']['anchors']; 191 if (!is_array($oldautolinks)) $oldautolinks = explode(' ', $oldautolinks); 192 foreach ($oldautolinks as $oldtag){ 193 if (!$oldtag) continue; // skip empty autolinks 194 $oldtag = utf8_strtolower($oldtag); 195// if (in_array($oldtag, $autolinks)) continue; // tag is still there 196 $this->autolink_idx[$oldtag]=""; 197 $changed = true; 198 } 199 200 } 201/* 202 // fill tag in 203 foreach ($autolinks as $autolink){ 204 if (!$autolink) continue; // skip empty autolinks 205 if ($this->autolink_idx[$autolink]!=$pid){ 206 $this->autolink_idx[$autolink] = $pid; 207 $changed = true; 208 } 209 } 210 */ 211 // save tag index 212 if ($changed) return $this->_saveIndex('autolink'); 213 else return true; 214 } 215 216 function getoldautolinks($thispid) { 217 $ali = $this->autolink_idx; 218 $retlinks = array(); 219 foreach($ali as $key=>$ind) { 220 if ($ind==$thispid) { 221 $retlinks[]=$key; 222 } 223 } 224 return $retlinks; 225 } 226 227 /** 228 * Save tag or page index 229 */ 230 function _saveIndex($idx = 'autolink'){ 231 $fh = fopen($this->idx_dir.'/'.$idx.'.idx', 'w'); 232 if (!$fh) return false; 233 if ($idx == 'page'){ 234 fwrite($fh, join('', $this->page_idx)); 235 } else { 236 $autolink_index = array(); 237 foreach ($this->autolink_idx as $key => $value){ 238 if ($value=="") continue; // skip empty autolinks 239 $autolink_index[] = $key."\t".$value."\n"; 240 } 241 fwrite($fh, join('', $autolink_index)); 242 } 243 fclose($fh); 244 return true; 245 } 246 247 /** 248 * Generates the autolink index 249 */ 250 function _generateAutolinkIndex(){ 251 global $conf; 252 require_once (DOKU_INC.'inc/search.php'); 253 $pages = array(); 254 search($pages, $conf['datadir'], 'search_allpages', array()); 255 foreach ($pages as $page){ 256 $anchors = p_get_metadata($page['id'], 'anchors'); 257 if (!is_array($anchors)) $anchors = explode('|', $anchors); 258 $this->_updateAutolinkIndex($page['id'], $anchors); 259 } 260 return true; 261 } 262 263 264 /** 265 * Converts an array of pages numbers to IDs 266 */ 267 function _numToID($nums){ 268 if (is_array($nums)){ 269 $docs = array(); 270 foreach ($nums as $page=>$num){ 271 $docs[$page] = trim($this->page_idx[$num]); 272 } 273 return $docs; 274 } else { 275 return trim($this->page_idx[$nums]); 276 } 277 } 278 279 /** 280 * Import old Autolink index 281 */ 282 function _importOldAutolinkIndex($to){ 283 global $conf; 284 $old=DOKU_PLUGIN.'autolink/data/links.php'; 285 $cache = $conf['cachedir'].'/autolink.idx'; 286 $index = $conf['indexdir'].'/autolink.idx'; 287 288 if ($to=='index') { 289 if (@file_exists($cache)) { 290 if (@copy($cache, $index)){ 291 @unlink($cache); 292 return true; 293 } 294 } else if (@file_exists($old)) { 295 return $this->_buildindexFromOld($index); 296 } else { 297 return $this->_generateAutolinkIndex(); 298 } 299 } else { 300 if (@file_exists($old)) { 301 return $this->_buildindexFromOld($cache); 302 } else { 303 $this->_generateAutolinkIndex(); 304 } 305 } 306 return false; 307 } 308 309 310 function _buildindexFromOld($to) { 311 global $conf; 312 $old=DOKU_PLUGIN.'autolink/data/links.php'; 313 require_once($old); 314 $changed = false; 315 foreach ($replace as $value){ 316 $page=substr($value,3,-2); 317 list ($pageid,$anchor) = explode('|',$page); 318 $pid = array_search("$pageid\n", $this->page_idx); 319 $this->autolink_idx[$anchor] = $pid; 320 $changed = true; 321 } 322 // save autolink index 323 if ($changed) return $this->_saveIndex('autolink'); 324 else return true; 325 } 326 327 328 329} 330