* Ideas "borrowed" from Esther Brunners tag plugin. * Version 8.3.2007 Fixed replace patter to use back reference to keep case of replaced text * Version 11.10.2011 Quote regular expression characters */ // must be run within Dokuwiki if (!defined('DOKU_INC')) die(); class helper_plugin_autolink2 extends DokuWiki_Plugin { var $idx_dir = ''; // directory for index files var $page_idx = array(); // array of existing pages var $autolink_idx = array(); // array of anchors and index in which pages they are found /** * Constructor gets default preferences and language strings */ function helper_plugin_autolink2(){ global $ID, $conf; // determine where index files are saved if (@file_exists($conf['indexdir'].'/page.idx')){ // new word length based index $this->idx_dir = $conf['indexdir']; $this->page_idx = @file($this->idx_dir.'/page.idx'); if (!@file_exists($this->idx_dir.'/autolink.idx')) $this->_importOldAutolinkIndex('index'); } else { // old index $this->idx_dir = $conf['cachedir']; $this->page_idx = @file($this->idx_dir.'/page.idx'); if (!@file_exists($this->idx_dir.'/autolink.idx')) $this->_importOldAutolinkIndex('cache'); } // load page and tag index $autolink_index = @file($this->idx_dir.'/autolink.idx'); usort($autolink_index,array("helper_plugin_autolink2","lensort")); if (is_array($autolink_index)){ foreach ($autolink_index as $idx_line){ list($key, $value) = explode("\t", $idx_line, 2); if ($value) { $this->autolink_idx[$key]=trim($value); } } } } function getInfo() { return confToHash(dirname(__FILE__).'/plugin.info.txt'); } function getMethods(){ $result = array(); $result[] = array( 'name' => 'getAnchors', 'desc' => 'returns pattern and replace arrays for replace', 'return' => array('{pattern,replace}' => 'array'), ); return $result; } function getAnchors() { $result = array(); // array of line numbers in the page index // $result = array_merge($result, $this->autolink_idx); $result = $this->autolink_idx; $res=$this->_numToID($result); if (is_array($res)){ $l=0; $sr="-anchorlink-"; $er="-knilrohcna-"; $sp="-anchorlink-"; $ep="-knilrohcna-"; $hasCustom=false; $customStart=""; $customEnd=""; if ($this->getConf('customfilter_start') && $this->getConf('customfilter_end')) { $customStart=$this->getConf('customfilter_start'); $customEnd=$this->getConf('customfilter_end'); $hasCustom=true; } foreach ($res as $anchor => $page){ // Mark everything close to possible as a potential autolink anchor $pattern[$l]="/(?<= |\\n|\\t|\|)(".$anchor.")(?=( |,|\.|:|\\n|\\t|\|))/msi"; // $replace[$l++]=$sr.$anchor.$er; $replace[$l++]=$sr."\\1".$er; // Remove anchor from headings $pattern[$l]="/(={1,6}.*?)".$sp."(".$anchor.")".$ep."(.*?={1,6})/i"; // $replace[$l++]="$1".$anchor."$3"; $replace[$l++]="$1$2$3"; // Remove anchor from media (and some plugin) refs = {{something}} $pattern[$l]="/(\{\{.*?)".$sp."(".$anchor.")".$ep."(.*?\}\})/i"; $replace[$l++]="$1$2$3"; // Remove anchor from links refs = [[something]] $pattern[$l]="/(\[\[.*)".$sp."(".$anchor.")".$ep."(.*\]\])/Ui"; $replace[$l++]="$1$2$3"; // Remove anchor from links refs = $pattern[$l]="/(\<.*)".$sp."(".$anchor.")".$ep."(.*\>)/Ui"; $replace[$l++]="$1$2$3"; // Remove custom pattern links. if ($hasCustom==true) { $pattern[$l]=$customStart.$sp."(".$anchor.")".$ep.$customEnd; $replace[$l++]="$1$2$3"; } // Finally change all that's left to links $pattern[$l]="/".$sp."(".$anchor.")".$ep."/i"; // $replace[$l++]="[[:".$page."|".$anchor."]]"; $replace[$l++]="[[:".$page."|$1]]"; } if (is_array($pattern)) ksort($pattern); if (is_array($replace)) ksort($replace); } // now convert to page IDs and return return array($pattern,$replace); } function lensort($a,$b){ return strlen($b)-strlen($a); } /** * Update Autolink index */ function _updateAutolinkIndex($id, $autolinks){ global $ID, $INFO; if (!is_array($autolinks) || empty($autolinks)) return false; usort($autolinks,array("helper_plugin_autolink2","lensort")); $changed = false; // get page id (this is the linenumber in page.idx) $pid = array_search("$id\n", $this->page_idx); if (!is_int($pid)){ $this->page_idx[] = "$id\n"; $pid = count($this->page_idx) - 1; // page was new - write back $this->_saveIndex('page'); } // clean array first $c = count($autolinks); for ($i = 0; $i <= $c; $i++){ $autolinks[$i] = utf8_strtolower(preg_quote($autolinks[$i], '/')); } // clear no longer used autolinks if ($ID == $id){ $oldautolinks = $INFO['meta']['anchors']; $oldautolinks = $this->getoldautolinks($pid); if (!is_array($oldautolinks)) $oldautolinks = explode(' ', $oldautolinks); foreach ($oldautolinks as $oldtag){ if (!$oldtag) continue; // skip empty autolinks $oldtag = utf8_strtolower($oldtag); if (in_array($oldtag, $autolinks)) continue; // tag is still there $this->autolink_idx[$oldtag]=""; $changed = true; } } // fill tag in foreach ($autolinks as $autolink){ if (!$autolink) continue; // skip empty autolinks if ($this->autolink_idx[$autolink]!=$pid){ $this->autolink_idx[$autolink] = $pid; $changed = true; } } // save tag index if ($changed) return $this->_saveIndex('autolink'); else return true; } /** * Remove Autolink index */ function _removeAutolinkIndex($id){ global $ID, $INFO; // get page id (this is the linenumber in page.idx) $pid = array_search("$id\n", $this->page_idx); if (!is_int($pid)){ return; } // clear no longer used autolinks if ($ID == $id){ $oldautolinks = $INFO['meta']['anchors']; if (!is_array($oldautolinks)) $oldautolinks = explode(' ', $oldautolinks); foreach ($oldautolinks as $oldtag){ if (!$oldtag) continue; // skip empty autolinks $oldtag = utf8_strtolower($oldtag); // if (in_array($oldtag, $autolinks)) continue; // tag is still there $this->autolink_idx[$oldtag]=""; $changed = true; } } /* // fill tag in foreach ($autolinks as $autolink){ if (!$autolink) continue; // skip empty autolinks if ($this->autolink_idx[$autolink]!=$pid){ $this->autolink_idx[$autolink] = $pid; $changed = true; } } */ // save tag index if ($changed) return $this->_saveIndex('autolink'); else return true; } function getoldautolinks($thispid) { $ali = $this->autolink_idx; $retlinks = array(); foreach($ali as $key=>$ind) { if ($ind==$thispid) { $retlinks[]=$key; } } return $retlinks; } /** * Save tag or page index */ function _saveIndex($idx = 'autolink'){ $fh = fopen($this->idx_dir.'/'.$idx.'.idx', 'w'); if (!$fh) return false; if ($idx == 'page'){ fwrite($fh, join('', $this->page_idx)); } else { $autolink_index = array(); foreach ($this->autolink_idx as $key => $value){ if ($value=="") continue; // skip empty autolinks $autolink_index[] = $key."\t".$value."\n"; } fwrite($fh, join('', $autolink_index)); } fclose($fh); return true; } /** * Generates the autolink index */ function _generateAutolinkIndex(){ global $conf; require_once (DOKU_INC.'inc/search.php'); $pages = array(); search($pages, $conf['datadir'], 'search_allpages', array()); foreach ($pages as $page){ $anchors = p_get_metadata($page['id'], 'anchors'); if (!is_array($anchors)) $anchors = explode('|', $anchors); $this->_updateAutolinkIndex($page['id'], $anchors); } return true; } /** * Converts an array of pages numbers to IDs */ function _numToID($nums){ if (is_array($nums)){ $docs = array(); foreach ($nums as $page=>$num){ $docs[$page] = trim($this->page_idx[$num]); } return $docs; } else { return trim($this->page_idx[$nums]); } } /** * Import old Autolink index */ function _importOldAutolinkIndex($to){ global $conf; $old=DOKU_PLUGIN.'autolink/data/links.php'; $cache = $conf['cachedir'].'/autolink.idx'; $index = $conf['indexdir'].'/autolink.idx'; if ($to=='index') { if (@file_exists($cache)) { if (@copy($cache, $index)){ @unlink($cache); return true; } } else if (@file_exists($old)) { return $this->_buildindexFromOld($index); } else { return $this->_generateAutolinkIndex(); } } else { if (@file_exists($old)) { return $this->_buildindexFromOld($cache); } else { $this->_generateAutolinkIndex(); } } return false; } function _buildindexFromOld($to) { global $conf; $old=DOKU_PLUGIN.'autolink/data/links.php'; require_once($old); $changed = false; foreach ($replace as $value){ $page=substr($value,3,-2); list ($pageid,$anchor) = explode('|',$page); $pid = array_search("$pageid\n", $this->page_idx); $this->autolink_idx[$anchor] = $pid; $changed = true; } // save autolink index if ($changed) return $this->_saveIndex('autolink'); else return true; } }