Extension:AutoLink

What can this extension do?
When a new page is created, or an existing page is saved, Some of the specific words in the page content will automatically become links to other pages. Such Extension was very much required for my application so i thought of placing it here so that others if interested can use it.

Version 1.x
Once this extension is installed, just edit a page, add some content to it and save the page. The words in the page content which are present in the page AutoLinkPages will become links to respective page. So AutoLinkPages acts as a dictionary for the Auto Link words.

Version 2.x
Words in a page's content that match any other Page Title will be displayed as links to that page. (It is no longer necessary to create an AutoLinkPage page.)

Version 3.7
Feature : Attach a Link which matches other Page Titles in a page's content.

Mode selection : Autolink during save page, Autolink during page views

Caution : This version has to list all Page Titles, so it may takes much CPU and resources when using some BIG wiki.

If v3.7 works as Autolink during page views mode, it never change its original content in the DB.

Installation

 * (1.x only)Create a page with name AutoLinkPages. This page will contain : separated pagenames which have to be auto linked. For example AutoLinkPages.
 * Just copy the extension directy from AutoLink.
 * Save it as AutoLink.php in extensions directory.

Changes to LocalSettings.php
Include this extension in LocalSettings.php as follows:-

Code
Note : Latest version at bottom.

version which matches all pages
",                       "", "",                        "", "",                        "[", "]",                        "",                        " ", " "                ),                $text,                'autoLinkWords'        );        return true; }

function mapIgnoringBlocks($delimiters, $text, $map) { if (count($delimiters) == 0) return $map($text);

$block_start = array_shift($delimiters); $block_end = array_shift($delimiters);

$result = ""; $post_headers = explode($block_start, $text);

$first_inter_block = array_shift($post_headers); $result = $result. mapIgnoringBlocks($delimiters, $first_inter_block, $map);

foreach($post_headers as $post_header) { $block_and_inter_block = explode($block_end, $post_header, 2);

$block = array_shift($block_and_inter_block); $result = $result. $block_start. $block. $block_end;

$inter_block = array_shift($block_and_inter_block); $result = $result. mapIgnoringBlocks($delimiters, $inter_block, $map); }

return $result; }

function autoLinkWords($text) { global $autolink__current_page;

$patterns = array; $replacements = array; foreach(getPages as $page) { if ($page != $autolink__current_page) { $pattern = str_replace('_', '[_ ]', preg_quote($page, '/')); $pattern = '/\b((?<!\[)(?<!\/\/)' . $pattern . '(?!\]))\b/i';

$patterns[] = $pattern; $replacements[] = '\1'; }       }        return preg_replace($patterns, $replacements, $text); }

function getPages { $dbr = &wfGetDB(DB_SLAVE); $result_set = $dbr->select('page', array('page_title'), array('page_namespace' => 0)); $titles = array; while (               ($row = $dbr->fetchObject($result_set))        ) { $titles[] = $row->page_title; }       $dbr->freeResult($result_set); return $titles; }
 * 1) Function to return page names from AutoLinkPages as an array

version which matches all pages giving preference to longer matches
",                       "", "",                        "", "",                        "[", "]",                        "",                        " ", " "                ),                $text        );        return true; }

function mapIgnoringBlocks($delimiters, $text, $pattern, $replacement) { if (count($delimiters) == 0) { return preg_replace($pattern, $replacement, $text); }

$block_start = array_shift($delimiters); $block_end = array_shift($delimiters);

$result = ""; $post_headers = explode($block_start, $text);

$first_inter_block = array_shift($post_headers); $result = $result. mapIgnoringBlocks($delimiters, $first_inter_block, $pattern, $replacement);

foreach($post_headers as $post_header) { $block_and_inter_block = explode($block_end, $post_header, 2);

$block = array_shift($block_and_inter_block); $result = $result. $block_start. $block. $block_end;

$inter_block = array_shift($block_and_inter_block); $result = $result. mapIgnoringBlocks($delimiters, $inter_block, $pattern, $replacement); }

return $result; }

function autoLinkWords($delimiters, $text) { global $autolink__current_page;

$patternMap = array; foreach(getPages as $page) { if ($page != $autolink__current_page) { $pattern = str_replace('_', '[_ ]', preg_quote($page, '/')); $pattern = '/\b((?<!\[)(?<!\/\/)' . $pattern . '(?!\]))\b/i';

$patternMap[$pattern] = '\1'; }       }        foreach($patternMap as $pattern=>$replacement) { $text = mapIgnoringBlocks($delimiters, $text, $pattern, $replacement); }       return $text; }

function getPages { $dbr = &wfGetDB(DB_SLAVE); $result_set = $dbr->select('page', array('page_title'), array('page_namespace' => 0), __METHOD__, array('ORDER BY' =>'page_title desc')); $titles = array; while (               ($row = $dbr->fetchObject($result_set))        ) { $titles[] = $row->page_title; }       $dbr->freeResult($result_set); return $titles; }
 * 1) Function to return page names from AutoLinkPages as an array

version which matches all pages preferring longer page names and provides shorter, easier to read links, add magic words
",                       "", "",                        "", "",                        "[", "]",                        "",                        " ", " "                ),                $text        );

return true; } function mapIgnoringBlocks($delimiters, $text, $pattern, $replacement, &$phrasecount) { if (count($delimiters) == 0) { if ($phrasecount < 2) { #make sure that we don't change the capitaliation of the string we are replacing $replaceResult = preg_replace($pattern,  . '\1' . , $text, 1);

if ($replaceResult != $text) { $phrasecount++; } return $replaceResult; }          else { return $text; }       }        $block_start = array_shift($delimiters); $block_end = array_shift($delimiters); $result = ""; $post_headers = explode($block_start, $text); $first_inter_block = array_shift($post_headers); $result = $result. mapIgnoringBlocks($delimiters, $first_inter_block, $pattern, $replacement, $phrasecount); foreach($post_headers as $post_header) { $block_and_inter_block = explode($block_end, $post_header, 2); $block = array_shift($block_and_inter_block); $result = $result. $block_start. $block. $block_end; $inter_block = array_shift($block_and_inter_block); $result = $result. mapIgnoringBlocks($delimiters, $inter_block, $pattern, $replacement, $phrasecount); }       return $result; } function autoLinkWords($delimiters, $text) { global $autolink__current_page;

foreach(getPages as $page) { if ($page != $autolink__current_page) { $pattern = str_replace('_', '[_ ]', preg_quote($page, '/')); $pattern = '/\b((?<!\[)(?<!\/\/)' . $pattern . '(?!\]))\b/i'; $match = '\1'; if ($match = $page) { $matchMap[$match] = 0; $match = str_replace('_',' ', $match); $patternMap[$pattern] =  . $match . ; }               }        }        foreach($patternMap as $pattern=>$replacement) { $tmpstr = str_replace(' ','_', $replacement); $index = substr($tmpstr,2,-2); $matchCount = $matchMap[$index]; $text = mapIgnoringBlocks($delimiters, $text, $pattern, $replacement, $matchCount); $matchMap[$index] = $matchCount; }

return $text; } function getPages { $dbr = &wfGetDB(DB_SLAVE); $result_set = $dbr->select('page', array('page_title'), array('page_namespace' => 0), __METHOD__, array('ORDER BY' =>'page_title desc')); $titles = array; while (($row = $dbr->fetchObject($result_set))) { $titles[] = $row->page_title; }       $dbr->freeResult($result_set); uasort($titles,"Ascii_sort"); //Longest page titles first return $titles; }
 * 1) Function to return page names from AutoLinkPages as an array

function Ascii_sort($val_1, $val_2) {       // initialize the return value to zero $retVal = 0; // compare lengths $firstVal = strlen($val_1); $secondVal = strlen($val_2); if($firstVal > $secondVal) { $retVal = -1; }       else if($firstVal < $secondVal) { $retVal = 1; }       return $retVal; }

//////////// Magic Word Functions ////////////

function AutolinkMagicWordMagicWords(&$magicWords) {       $magicWords[] = 'MAG_NOAUTOLINK'; $magicWords[] = 'MAG_NOREGENERATELINKS'; return true; } function AutolinkMagicWordwgVariableIDs(&$wgVariableIDs) {       $wgVariableIDs[] = MAG_NOAUTOLINK; $wgVariableIDs[] = MAG_NOREGENERATELINKS; return true; } function AutolinkLanguageGetMagic(&$magicWords, $langCode) {       $magicWords[MAG_NOAUTOLINK] = array( 0, '__NOAUTOLINK__'); $magicWords[MAG_NOREGENERATELINKS] = array( 0, '__NOREGENERATELINKS__' ); return true; } function AutolinkParserBeforeInternalParse($parser, $text, $stripState) {       if (MagicWord::get( MAG_NOAUTOLINK )->matchAndRemove( $text ) ) {               $parser->mOptions->mNumberHeadings = (TRUE); }       if (MagicWord::get( MAG_NOREGENERATELINKS)->matchAndRemove( $text ) ) {               $parser->mOptions->mNumberHeadings = (TRUE); }       return true; }

Added Feature Autolink during page views
"),		array("", ""),		array("#REDIRECT", "]]"),		);

$currentPage = str_replace(" ", "_", $title);

# If we specificy no autolink, than don't autolink if (stripos($text, '__NOAUTOLINK__') !== false) { return true; }

# As long as we are allowed to regenerate links... if (stripos($text, '__NOREGENERATELINKS__') === false) {		# Strip links so that all links are regenerated except those that are custom links # ie have a '|' or ')' or '(' in them # It only work for odd number region(Protected area). $region = AutoLink_MakeRegion($text, $ignoreBlocks);

for ( $i = 1; $i < count($region); $i += 2) {			$region[$i] = preg_replace('/^\[\[([^|:\(\)\]]*)\]\]$/','\1',$region[$i]); }

# Merge regions so that all stripped links are unprotected. $text = implode($region); }

$titles = AutoLink_GetTitleList($currentPage); AutoLink_MakeLink($titles, $text, $ignoreBlocks);

return true; }

function AutoLink_GetTitleList($currentPage) {	$dbr = &wfGetDB(DB_SLAVE); $result = $dbr->select('page', array('page_title'), 'page_namespace=0 AND page_title <> "'.$currentPage.'"', __METHOD__, array('ORDER BY' =>'CHAR_LENGTH(page_title) DESC'));

$titles = array;

while (($row = $dbr->fetchObject($result))) {		$titles[] = $row->page_title; }	$dbr->freeResult($result);

return $titles; }

function AutoLink_FindFirstSkipBlock(&$ignoreBlocks, &$text) {	$startTag = ""; $endTag = ""; $firstTag_start = strlen($text) + 1; # sentinel value

foreach($ignoreBlocks as $tagPair) {		$curTag_start = stripos($text, $tagPair[0]); if ( $curTag_start !== false && $firstTag_start > $curTag_start ) {			$startTag = $tagPair[0]; $endTag = $tagPair[1]; $firstTag_start = $curTag_start; }	}

if ( $firstTag_start > strlen($text) ) return false;

$startTagLength = strlen($startTag); $endTagLength = strlen($endTag); $firstTag_end = stripos($text, $endTag, $firstTag_start + $startTagLength); $nextPosition = $firstTag_start + $startTagLength; $tagDepth = 0; while ( true ) {		$findPosition = stripos($text, $startTag, $nextPosition); if ( $findPosition === false || $findPosition > $firstTag_end ) {			break; }

$nextPosition = $findPosition + $startTagLength; ++$tagDepth; }	$nextPosition = $firstTag_end + $endTagLength;

while ( $tagDepth > 0 ) {		$findPosition = stripos($text, $endTag, $nextPosition);

# If orpant tag has been detected, whole block is selected. if ( $findPosition === false ) {			$nextPosition = strlen($text); break; }

$nextPosition = $findPosition + $endTagLength; --$tagDepth; }

$firstTag_end = $nextPosition;

return array($firstTag_start, $firstTag_end); }

function AutoLink_MakeRegion(&$text, &$ignoreBlocks) {	$textLength = strlen($text); # Region :: Even : Normal text area, Odd : Protected area(skip autolink) $region = array; $skipBlock = AutoLink_FindFirstSkipBlock($ignoreBlocks, $text); # Recursion Terminate condition if ( $skipBlock === false ) {		return array($text); }	$splitBegin = $skipBlock[0]; $splitEnd = $skipBlock[1]; $region = array_merge(		array(substr($text, 0, $splitBegin), substr($text, $splitBegin, $splitEnd - $splitBegin)),		AutoLink_MakeRegion(substr($text, $splitEnd, $textLength - $splitEnd), $ignoreBlocks)		); return $region; } function AutoLink_MakeLink(&$titles, &$text, &$ignoreBlocks) {	$autoLinkTag = array(array("","")); $region = AutoLink_MakeRegion($text, $ignoreBlocks); foreach($titles as $page) {		$pattern = str_replace('_','[_ ]', preg_quote($page, '/')); $patternLength = strlen($page); $replace = .str_replace('_', ' ', $page).; # Make links. It only work for even number region. # Odd number region is ignoring block(include already linked text) for ( $i = 0; $i < count($region); ) {			if ( strlen($region[$i]) < $patternLength ) {				$i += 2; continue; }			$region[$i] = preg_replace('/'.$pattern.'/i', $replace, $region[$i]); $newRegion = AutoLink_MakeRegion($region[$i], $autoLinkTag); array_splice($region, $i, 1, $newRegion); $i += count($newRegion) + 1; }	}	$text = implode($region); } ?>

Contact
In case of any issues please get in touch with Sanjeev. Thanks!