r40361 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r40360‎ | r40361 | r40362 >
Date:06:08, 3 September 2008
Author:jdpond
Status:old
Tags:
Comment:
Publish DiscussionThreading Extension
Modified paths:
  • /trunk/extensions/DiscussionThreading (added) (history)
  • /trunk/extensions/DiscussionThreading/DiscussionThreading.i18n.php (added) (history)
  • /trunk/extensions/DiscussionThreading/DiscussionThreading.php (added) (history)
  • /trunk/extensions/DiscussionThreading/README (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_10_0 (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_10_0/DiscussionThreading1_10.patch (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_10_0/includes (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_10_0/includes/Linker.php (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_10_1 (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_10_1/DiscussionThreading1_10_1.patch (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_10_1/includes (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_10_1/includes/Linker.php (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_9_3 (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_9_3/DiscussionThreading1_9_3.patch (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_9_3/includes (added) (history)
  • /trunk/extensions/DiscussionThreading/REV1_9_3/includes/Linker.php (added) (history)

Diff [purge]

Index: trunk/extensions/DiscussionThreading/DiscussionThreading.php
@@ -0,0 +1,177 @@
 2+<?php
 3+
 4+/**
 5+ * Extension to provide discussion threading similar to a listserv archive
 6+ *
 7+ * @author Jack D. Pond <jack.pond@psitex.com>
 8+ * @addtogroup Extensions
 9+ * @copyright � 2007 Jack D. pond
 10+ * @licence GNU General Public Licence 2.0 or later
 11+ */
 12+
 13+if( defined( 'MEDIAWIKI' ) ) {
 14+
 15+
 16+# Internationalisation file
 17+ require_once( "$IP/extensions/DiscussionThreading/DiscussionThreading.i18n.php" );
 18+
 19+ $wgExtensionFunctions[] = 'efDiscussionThreadSetup';
 20+ $wgExtensionCredits['other'][] = array(
 21+ 'name' => 'DiscussionThreading',
 22+ 'author' => 'Jack D. Pond',
 23+ 'url' => 'http://www.mediawiki.org/wiki/Extension:DiscussionThreading',
 24+ 'description' => 'Add Threading to discussion (talk) pages' );
 25+
 26+ /**
 27+ * Set up hooks for discussion threading
 28+ *
 29+ * @param $wgSectionThreadingOn global logical variable to activate threading
 30+ */
 31+
 32+
 33+ global $wgSectionThreadingOn;
 34+ $wgSectionThreadingOn = True;
 35+
 36+ $wgHooks['EditPage::showEditForm:initial'][] = 'efDiscussionThread';
 37+ $wgHooks['EditPage::attemptSave'][] = 'efStampReply';
 38+ $wgHooks['EditPage::showEditForm:initial'][] = 'efDiscussionThreadEdit';
 39+ $wgHooks['EditSectionLinkForOther'][] = 'efDiscussionLink4other';
 40+ $wgHooks['EditSectionLink'][] = 'efDiscussionLink';
 41+ $wgHooks['AlternateEdit'][] = 'efDiscussionThreadEdit';
 42+
 43+
 44+
 45+
 46+ /**
 47+ * Initial setup, add .i18n. messages from $IP/extensions/DiscussionThreading.i18n.php
 48+ */
 49+ function efDiscussionThreadSetup() {
 50+ global $wgMessageCache, $wgDiscussionThreadMessages;
 51+
 52+ foreach( $wgDiscussionThreadMessages as $lang => $messages )
 53+ $wgMessageCache->addMessages( $messages, $lang );
 54+ }
 55+ /**
 56+ * This function creates a linkobject for the editSectionLinkForOther function in linker
 57+ *
 58+ * @param $callobj Article object.
 59+ * @param $title Title object.
 60+ * @param $section Integer: section number.
 61+ * @param $hint Link String: title, or default if omitted or empty
 62+ * @param $url Link String: for edit url
 63+ * @param $result String: Returns the section [new][edit][reply] html if in a talk page - otherwise whatever came in with
 64+ * @return true
 65+ */
 66+
 67+ function efDiscussionLink4other ($callobj, $title, $section , $url , &$result)
 68+ {
 69+ global $wgSectionThreadingOn;
 70+ if($wgSectionThreadingOn && $title->isTalkPage() ) {
 71+ $commenturl = '&section='.$section.'&replyto=yes';
 72+ $curl = $callobj->makeKnownLinkObj( $title, wfMsg('replysection'), 'action=edit'.$commenturl );
 73+ $newthreadurl = '&section=new';
 74+ $nurl = $callobj->makeKnownLinkObj( $nt, wfMsg('threadnewsection'), 'action=edit'.$newthreadurl );
 75+ $result = $nurl."][".$url."][".$curl;
 76+ }
 77+ return (true);
 78+ }
 79+
 80+ /**
 81+ * This function creates a linkobject for the editSectionLink function in linker
 82+ *
 83+ * @param $callobj Article object.
 84+ * @param $nt Title object.
 85+ * @param $section Integer: section number.
 86+ * @param $hint Link String: title, or default if omitted or empty
 87+ * @param $url Link String: for edit url
 88+ * @param $result String: Returns the section [new][edit][reply] html if in a talk page - otherwise whatever came in with
 89+ * @return true
 90+ */
 91+
 92+ function efDiscussionLink ($callobj, $nt, $section, $hint='', $url , &$result)
 93+ {
 94+ global $wgSectionThreadingOn;
 95+ if($wgSectionThreadingOn && $nt->isTalkPage() ) {
 96+ $commenturl = '&section='.$section.'&replyto=yes';
 97+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'replysectionhint', htmlspecialchars( $hint ) ) . '"';
 98+ $curl = $callobj->makeKnownLinkObj( $nt, wfMsg('replysection'), 'action=edit'.$commenturl, '', '', '', $hint );
 99+ $newthreadurl = '&section=new';
 100+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'threadnewsectionhint', htmlspecialchars( $hint ) ) . '"';
 101+ $nurl = $callobj->makeKnownLinkObj( $nt, wfMsg('threadnewsection'), 'action=edit'.$newthreadurl, '', '', '', $hint );
 102+ $result = $nurl."][".$url."][".$curl;
 103+ }
 104+ return (true);
 105+ }
 106+
 107+ /**
 108+ * This function is a hook used to test to see if empty, if so, start a comment
 109+ *
 110+ * @param $efform form object.
 111+ * @return true
 112+ */
 113+
 114+
 115+ function efDiscussionThreadEdit ($efform) {
 116+ global $wgRequest,$wgSectionThreadingOn;
 117+ $efform->replytosection = '';
 118+ $efform->replyadded = false;
 119+ $efform->replytosection = $wgRequest->getVal( 'replyto' );
 120+ if( !$efform->mTitle->exists() ) {
 121+ if($wgSectionThreadingOn && $efform->mTitle->isTalkPage() ) {
 122+ $efform->section = 'new';
 123+ }
 124+ }
 125+ return (true);
 126+ }
 127+
 128+ /**
 129+ * Create a new header, one level below the 'replyto' header, add re: to front and tag it with user information
 130+ *
 131+ * @param $efform Form Object before display
 132+ * @return true
 133+ */
 134+
 135+ function efDiscussionThread($efform){
 136+ global $wgSectionThreadingOn;
 137+ $wgSectionThreadingOn = isset($wgSectionThreadingOn) ? $wgSectionThreadingOn : false;
 138+ if ( $efform->replytosection != '' && $wgSectionThreadingOn && !$efform->replyadded) {
 139+ if ($efform->replytosection != '') {
 140+ $text = $efform->textbox1;
 141+ $matches = array();
 142+ preg_match( "/^(=+)(.+)\\1/mi",
 143+ $efform->textbox1,
 144+ $matches );
 145+ if( !empty( $matches[2] ) ) {
 146+ preg_match( "/.*(-+)\\1/mi",$matches[2],$matchsign);
 147+ if (!empty($matchsign[0]) ){
 148+ $text = $text."\n\n".$matches[1]."=Re: ".trim($matchsign[0])." ~~~~".$matches[1]."=";
 149+ } else {
 150+ $text = $text."\n\n".$matches[1]."=Re: ".trim($matches[2])." -- ~~~~".$matches[1]."=";
 151+ }
 152+ } else {
 153+ $text = $text." -- ~~~~<br>\n\n";
 154+ }
 155+ $efform->replyadded = true;
 156+ $efform->textbox1 = $text;
 157+ }
 158+ return (true);
 159+ }
 160+ return (true);
 161+ }
 162+ /**
 163+ * When the new header is created from summary in new (+) add comment, just stamp the header as created
 164+ *
 165+ * @param $efform Form Object before display
 166+ * @return true
 167+ */
 168+
 169+ function efStampReply($efform){
 170+ global $wgSectionThreadingOn;
 171+ $wgSectionThreadingOn = isset($wgSectionThreadingOn) ? $wgSectionThreadingOn : false;
 172+ if ( $efform->section == "new" && $wgSectionThreadingOn && !$efform->replyadded) {
 173+ $efform->summary = $efform->summary." -- ~~~~";
 174+ }
 175+ return(true);
 176+ }
 177+}
 178+?>
\ No newline at end of file
Index: trunk/extensions/DiscussionThreading/REV1_9_3/DiscussionThreading1_9_3.patch
@@ -0,0 +1,37 @@
 2+Index: Linker.php
 3+===================================================================
 4+--- Linker.php (revision 22144)
 5+@@ -1038,8 +1038,14 @@
 6+ $editurl = '&section='.$section;
 7+ $url = $this->makeKnownLinkObj( $title, wfMsg('editsection'), 'action=edit'.$editurl );
 8+
 9+- return "<span class=\"editsection\">[".$url."]</span>";
 10+-
 11++### - return "<span class=\"editsection\">[".$url."]</span>";
 12++ /** Added editSectionLinkForOther hook that allows section/header link to be modified */
 13++ $result = null;
 14++ wfRunHooks( 'EditSectionLinkForOther', array( &$this, $title, $section, $url, &$result ) );
 15++ return is_null( $result )
 16++ ? "<span class=\"editsection\">[{$url}]</span>"
 17++ : "<span class=\"editsection\">[{$result}]</span>";
 18++ /** End of hook */
 19+ }
 20+
 21+ /**
 22+@@ -1054,7 +1060,14 @@
 23+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) ) . '"';
 24+ $url = $this->makeKnownLinkObj( $nt, wfMsg('editsection'), 'action=edit'.$editurl, '', '', '', $hint );
 25+
 26+- return "<span class=\"editsection\">[".$url."]</span>";
 27++### - return "<span class=\"editsection\">[".$url."]</span>";
 28++ /** Added editSectionLink hook that allows section/header link to be modified */
 29++ $result = null;
 30++ wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $hint, $url, &$result ) );
 31++ return is_null( $result )
 32++ ? "<span class=\"editsection\">[{$url}]</span>"
 33++ : "<span class=\"editsection\">[{$result}]</span>";
 34++ /** End of hook */
 35+ }
 36+
 37+ /**
Index: trunk/extensions/DiscussionThreading/REV1_9_3/includes/Linker.php
@@ -0,0 +1,1220 @@
 2+<?php
 3+/**
 4+ * Split off some of the internal bits from Skin.php.
 5+ * These functions are used for primarily page content:
 6+ * links, embedded images, table of contents. Links are
 7+ * also used in the skin.
 8+ * @package MediaWiki
 9+ */
 10+
 11+/**
 12+ * For the moment, Skin is a descendent class of Linker.
 13+ * In the future, it should probably be further split
 14+ * so that ever other bit of the wiki doesn't have to
 15+ * go loading up Skin to get at it.
 16+ *
 17+ * @package MediaWiki
 18+ */
 19+class Linker {
 20+ function Linker() {}
 21+
 22+ /**
 23+ * @deprecated
 24+ */
 25+ function postParseLinkColour( $s = NULL ) {
 26+ return NULL;
 27+ }
 28+
 29+ /** @todo document */
 30+ function getExternalLinkAttributes( $link, $text, $class='' ) {
 31+ $link = htmlspecialchars( $link );
 32+
 33+ $r = ($class != '') ? " class=\"$class\"" : " class=\"external\"";
 34+
 35+ $r .= " title=\"{$link}\"";
 36+ return $r;
 37+ }
 38+
 39+ function getInterwikiLinkAttributes( $link, $text, $class='' ) {
 40+ global $wgContLang;
 41+
 42+ $link = urldecode( $link );
 43+ $link = $wgContLang->checkTitleEncoding( $link );
 44+ $link = preg_replace( '/[\\x00-\\x1f]/', ' ', $link );
 45+ $link = htmlspecialchars( $link );
 46+
 47+ $r = ($class != '') ? " class=\"$class\"" : " class=\"external\"";
 48+
 49+ $r .= " title=\"{$link}\"";
 50+ return $r;
 51+ }
 52+
 53+ /** @todo document */
 54+ function getInternalLinkAttributes( $link, $text, $broken = false ) {
 55+ $link = urldecode( $link );
 56+ $link = str_replace( '_', ' ', $link );
 57+ $link = htmlspecialchars( $link );
 58+
 59+ if( $broken == 'stub' ) {
 60+ $r = ' class="stub"';
 61+ } else if ( $broken == 'yes' ) {
 62+ $r = ' class="new"';
 63+ } else {
 64+ $r = '';
 65+ }
 66+
 67+ $r .= " title=\"{$link}\"";
 68+ return $r;
 69+ }
 70+
 71+ /**
 72+ * @param $nt Title object.
 73+ * @param $text String: FIXME
 74+ * @param $broken Boolean: FIXME, default 'false'.
 75+ */
 76+ function getInternalLinkAttributesObj( &$nt, $text, $broken = false ) {
 77+ if( $broken == 'stub' ) {
 78+ $r = ' class="stub"';
 79+ } else if ( $broken == 'yes' ) {
 80+ $r = ' class="new"';
 81+ } else {
 82+ $r = '';
 83+ }
 84+
 85+ $r .= ' title="' . $nt->getEscapedText() . '"';
 86+ return $r;
 87+ }
 88+
 89+ /**
 90+ * This function is a shortcut to makeLinkObj(Title::newFromText($title),...). Do not call
 91+ * it if you already have a title object handy. See makeLinkObj for further documentation.
 92+ *
 93+ * @param $title String: the text of the title
 94+ * @param $text String: link text
 95+ * @param $query String: optional query part
 96+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 97+ * be included in the link text. Other characters will be appended after
 98+ * the end of the link.
 99+ */
 100+ function makeLink( $title, $text = '', $query = '', $trail = '' ) {
 101+ wfProfileIn( 'Linker::makeLink' );
 102+ $nt = Title::newFromText( $title );
 103+ if ($nt) {
 104+ $result = $this->makeLinkObj( Title::newFromText( $title ), $text, $query, $trail );
 105+ } else {
 106+ wfDebug( 'Invalid title passed to Linker::makeLink(): "'.$title."\"\n" );
 107+ $result = $text == "" ? $title : $text;
 108+ }
 109+
 110+ wfProfileOut( 'Linker::makeLink' );
 111+ return $result;
 112+ }
 113+
 114+ /**
 115+ * This function is a shortcut to makeKnownLinkObj(Title::newFromText($title),...). Do not call
 116+ * it if you already have a title object handy. See makeKnownLinkObj for further documentation.
 117+ *
 118+ * @param $title String: the text of the title
 119+ * @param $text String: link text
 120+ * @param $query String: optional query part
 121+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 122+ * be included in the link text. Other characters will be appended after
 123+ * the end of the link.
 124+ */
 125+ function makeKnownLink( $title, $text = '', $query = '', $trail = '', $prefix = '',$aprops = '') {
 126+ $nt = Title::newFromText( $title );
 127+ if ($nt) {
 128+ return $this->makeKnownLinkObj( Title::newFromText( $title ), $text, $query, $trail, $prefix , $aprops );
 129+ } else {
 130+ wfDebug( 'Invalid title passed to Linker::makeKnownLink(): "'.$title."\"\n" );
 131+ return $text == '' ? $title : $text;
 132+ }
 133+ }
 134+
 135+ /**
 136+ * This function is a shortcut to makeBrokenLinkObj(Title::newFromText($title),...). Do not call
 137+ * it if you already have a title object handy. See makeBrokenLinkObj for further documentation.
 138+ *
 139+ * @param string $title The text of the title
 140+ * @param string $text Link text
 141+ * @param string $query Optional query part
 142+ * @param string $trail Optional trail. Alphabetic characters at the start of this string will
 143+ * be included in the link text. Other characters will be appended after
 144+ * the end of the link.
 145+ */
 146+ function makeBrokenLink( $title, $text = '', $query = '', $trail = '' ) {
 147+ $nt = Title::newFromText( $title );
 148+ if ($nt) {
 149+ return $this->makeBrokenLinkObj( Title::newFromText( $title ), $text, $query, $trail );
 150+ } else {
 151+ wfDebug( 'Invalid title passed to Linker::makeBrokenLink(): "'.$title."\"\n" );
 152+ return $text == '' ? $title : $text;
 153+ }
 154+ }
 155+
 156+ /**
 157+ * This function is a shortcut to makeStubLinkObj(Title::newFromText($title),...). Do not call
 158+ * it if you already have a title object handy. See makeStubLinkObj for further documentation.
 159+ *
 160+ * @param $title String: the text of the title
 161+ * @param $text String: link text
 162+ * @param $query String: optional query part
 163+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 164+ * be included in the link text. Other characters will be appended after
 165+ * the end of the link.
 166+ */
 167+ function makeStubLink( $title, $text = '', $query = '', $trail = '' ) {
 168+ $nt = Title::newFromText( $title );
 169+ if ($nt) {
 170+ return $this->makeStubLinkObj( Title::newFromText( $title ), $text, $query, $trail );
 171+ } else {
 172+ wfDebug( 'Invalid title passed to Linker::makeStubLink(): "'.$title."\"\n" );
 173+ return $text == '' ? $title : $text;
 174+ }
 175+ }
 176+
 177+ /**
 178+ * Make a link for a title which may or may not be in the database. If you need to
 179+ * call this lots of times, pre-fill the link cache with a LinkBatch, otherwise each
 180+ * call to this will result in a DB query.
 181+ *
 182+ * @param $nt Title: the title object to make the link from, e.g. from
 183+ * Title::newFromText.
 184+ * @param $text String: link text
 185+ * @param $query String: optional query part
 186+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 187+ * be included in the link text. Other characters will be appended after
 188+ * the end of the link.
 189+ * @param $prefix String: optional prefix. As trail, only before instead of after.
 190+ */
 191+ function makeLinkObj( $nt, $text= '', $query = '', $trail = '', $prefix = '' ) {
 192+ global $wgUser;
 193+ $fname = 'Linker::makeLinkObj';
 194+ wfProfileIn( $fname );
 195+
 196+ # Fail gracefully
 197+ if ( ! is_object($nt) ) {
 198+ # throw new MWException();
 199+ wfProfileOut( $fname );
 200+ return "<!-- ERROR -->{$prefix}{$text}{$trail}";
 201+ }
 202+
 203+ if ( $nt->isExternal() ) {
 204+ $u = $nt->getFullURL();
 205+ $link = $nt->getPrefixedURL();
 206+ if ( '' == $text ) { $text = $nt->getPrefixedText(); }
 207+ $style = $this->getInterwikiLinkAttributes( $link, $text, 'extiw' );
 208+
 209+ $inside = '';
 210+ if ( '' != $trail ) {
 211+ $m = array();
 212+ if ( preg_match( '/^([a-z]+)(.*)$$/sD', $trail, $m ) ) {
 213+ $inside = $m[1];
 214+ $trail = $m[2];
 215+ }
 216+ }
 217+ $t = "<a href=\"{$u}\"{$style}>{$text}{$inside}</a>";
 218+
 219+ wfProfileOut( $fname );
 220+ return $t;
 221+ } elseif ( $nt->isAlwaysKnown() ) {
 222+ # Image links, special page links and self-links with fragements are always known.
 223+ $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
 224+ } else {
 225+ wfProfileIn( $fname.'-immediate' );
 226+ # Work out link colour immediately
 227+ $aid = $nt->getArticleID() ;
 228+ if ( 0 == $aid ) {
 229+ $retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix );
 230+ } else {
 231+ $threshold = $wgUser->getOption('stubthreshold') ;
 232+ if ( $threshold > 0 ) {
 233+ $dbr =& wfGetDB( DB_SLAVE );
 234+ $s = $dbr->selectRow(
 235+ array( 'page' ),
 236+ array( 'page_len',
 237+ 'page_namespace',
 238+ 'page_is_redirect' ),
 239+ array( 'page_id' => $aid ), $fname ) ;
 240+ if ( $s !== false ) {
 241+ $size = $s->page_len;
 242+ if ( $s->page_is_redirect OR $s->page_namespace != NS_MAIN ) {
 243+ $size = $threshold*2 ; # Really big
 244+ }
 245+ } else {
 246+ $size = $threshold*2 ; # Really big
 247+ }
 248+ } else {
 249+ $size = 1 ;
 250+ }
 251+ if ( $size < $threshold ) {
 252+ $retVal = $this->makeStubLinkObj( $nt, $text, $query, $trail, $prefix );
 253+ } else {
 254+ $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
 255+ }
 256+ }
 257+ wfProfileOut( $fname.'-immediate' );
 258+ }
 259+ wfProfileOut( $fname );
 260+ return $retVal;
 261+ }
 262+
 263+ /**
 264+ * Make a link for a title which definitely exists. This is faster than makeLinkObj because
 265+ * it doesn't have to do a database query. It's also valid for interwiki titles and special
 266+ * pages.
 267+ *
 268+ * @param $nt Title object of target page
 269+ * @param $text String: text to replace the title
 270+ * @param $query String: link target
 271+ * @param $trail String: text after link
 272+ * @param $prefix String: text before link text
 273+ * @param $aprops String: extra attributes to the a-element
 274+ * @param $style String: style to apply - if empty, use getInternalLinkAttributesObj instead
 275+ * @return the a-element
 276+ */
 277+ function makeKnownLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) {
 278+
 279+ $fname = 'Linker::makeKnownLinkObj';
 280+ wfProfileIn( $fname );
 281+
 282+ if ( !is_object( $nt ) ) {
 283+ wfProfileOut( $fname );
 284+ return $text;
 285+ }
 286+
 287+ $u = $nt->escapeLocalURL( $query );
 288+ if ( $nt->getFragment() != '' ) {
 289+ if( $nt->getPrefixedDbkey() == '' ) {
 290+ $u = '';
 291+ if ( '' == $text ) {
 292+ $text = htmlspecialchars( $nt->getFragment() );
 293+ }
 294+ }
 295+ $u .= $nt->getFragmentForURL();
 296+ }
 297+ if ( $text == '' ) {
 298+ $text = htmlspecialchars( $nt->getPrefixedText() );
 299+ }
 300+ if ( $style == '' ) {
 301+ $style = $this->getInternalLinkAttributesObj( $nt, $text );
 302+ }
 303+
 304+ if ( $aprops !== '' ) $aprops = ' ' . $aprops;
 305+
 306+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 307+ $r = "<a href=\"{$u}\"{$style}{$aprops}>{$prefix}{$text}{$inside}</a>{$trail}";
 308+ wfProfileOut( $fname );
 309+ return $r;
 310+ }
 311+
 312+ /**
 313+ * Make a red link to the edit page of a given title.
 314+ *
 315+ * @param $title String: The text of the title
 316+ * @param $text String: Link text
 317+ * @param $query String: Optional query part
 318+ * @param $trail String: Optional trail. Alphabetic characters at the start of this string will
 319+ * be included in the link text. Other characters will be appended after
 320+ * the end of the link.
 321+ */
 322+ function makeBrokenLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 323+ # Fail gracefully
 324+ if ( ! isset($nt) ) {
 325+ # throw new MWException();
 326+ return "<!-- ERROR -->{$prefix}{$text}{$trail}";
 327+ }
 328+
 329+ $fname = 'Linker::makeBrokenLinkObj';
 330+ wfProfileIn( $fname );
 331+
 332+ if ( '' == $query ) {
 333+ $q = 'action=edit';
 334+ } else {
 335+ $q = 'action=edit&'.$query;
 336+ }
 337+ $u = $nt->escapeLocalURL( $q );
 338+
 339+ if ( '' == $text ) {
 340+ $text = htmlspecialchars( $nt->getPrefixedText() );
 341+ }
 342+ $style = $this->getInternalLinkAttributesObj( $nt, $text, "yes" );
 343+
 344+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 345+ $s = "<a href=\"{$u}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
 346+
 347+ wfProfileOut( $fname );
 348+ return $s;
 349+ }
 350+
 351+ /**
 352+ * Make a brown link to a short article.
 353+ *
 354+ * @param $title String: the text of the title
 355+ * @param $text String: link text
 356+ * @param $query String: optional query part
 357+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 358+ * be included in the link text. Other characters will be appended after
 359+ * the end of the link.
 360+ */
 361+ function makeStubLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 362+ $u = $nt->escapeLocalURL( $query );
 363+
 364+ if ( '' == $text ) {
 365+ $text = htmlspecialchars( $nt->getPrefixedText() );
 366+ }
 367+ $style = $this->getInternalLinkAttributesObj( $nt, $text, 'stub' );
 368+
 369+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 370+ $s = "<a href=\"{$u}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
 371+ return $s;
 372+ }
 373+
 374+ /**
 375+ * Generate either a normal exists-style link or a stub link, depending
 376+ * on the given page size.
 377+ *
 378+ * @param $size Integer
 379+ * @param $nt Title object.
 380+ * @param $text String
 381+ * @param $query String
 382+ * @param $trail String
 383+ * @param $prefix String
 384+ * @return string HTML of link
 385+ */
 386+ function makeSizeLinkObj( $size, $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 387+ global $wgUser;
 388+ $threshold = intval( $wgUser->getOption( 'stubthreshold' ) );
 389+ if( $size < $threshold ) {
 390+ return $this->makeStubLinkObj( $nt, $text, $query, $trail, $prefix );
 391+ } else {
 392+ return $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
 393+ }
 394+ }
 395+
 396+ /**
 397+ * Make appropriate markup for a link to the current article. This is currently rendered
 398+ * as the bold link text. The calling sequence is the same as the other make*LinkObj functions,
 399+ * despite $query not being used.
 400+ */
 401+ function makeSelfLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 402+ if ( '' == $text ) {
 403+ $text = htmlspecialchars( $nt->getPrefixedText() );
 404+ }
 405+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 406+ return "<strong class=\"selflink\">{$prefix}{$text}{$inside}</strong>{$trail}";
 407+ }
 408+
 409+ /** @todo document */
 410+ function fnamePart( $url ) {
 411+ $basename = strrchr( $url, '/' );
 412+ if ( false === $basename ) {
 413+ $basename = $url;
 414+ } else {
 415+ $basename = substr( $basename, 1 );
 416+ }
 417+ return htmlspecialchars( $basename );
 418+ }
 419+
 420+ /** Obsolete alias */
 421+ function makeImage( $url, $alt = '' ) {
 422+ return $this->makeExternalImage( $url, $alt );
 423+ }
 424+
 425+ /** @todo document */
 426+ function makeExternalImage( $url, $alt = '' ) {
 427+ if ( '' == $alt ) {
 428+ $alt = $this->fnamePart( $url );
 429+ }
 430+ $s = '<img src="'.$url.'" alt="'.$alt.'" />';
 431+ return $s;
 432+ }
 433+
 434+ /** @todo document */
 435+ function makeImageLinkObj( $nt, $label, $alt, $align = '', $width = false, $height = false, $framed = false,
 436+ $thumb = false, $manual_thumb = '', $page = null )
 437+ {
 438+ global $wgContLang, $wgUser, $wgThumbLimits, $wgGenerateThumbnailOnParse;
 439+
 440+ $img = new Image( $nt );
 441+
 442+ if ( ! is_null( $page ) ) {
 443+ $img->selectPage( $page );
 444+ }
 445+
 446+ if ( !$img->allowInlineDisplay() && $img->exists() ) {
 447+ return $this->makeKnownLinkObj( $nt );
 448+ }
 449+
 450+ $url = $img->getViewURL();
 451+ $error = $prefix = $postfix = '';
 452+
 453+ wfDebug( "makeImageLinkObj: '$width'x'$height', \"$label\"\n" );
 454+
 455+ if ( 'center' == $align )
 456+ {
 457+ $prefix = '<div class="center">';
 458+ $postfix = '</div>';
 459+ $align = 'none';
 460+ }
 461+
 462+ if ( $thumb || $framed ) {
 463+
 464+ # Create a thumbnail. Alignment depends on language
 465+ # writing direction, # right aligned for left-to-right-
 466+ # languages ("Western languages"), left-aligned
 467+ # for right-to-left-languages ("Semitic languages")
 468+ #
 469+ # If thumbnail width has not been provided, it is set
 470+ # to the default user option as specified in Language*.php
 471+ if ( $align == '' ) {
 472+ $align = $wgContLang->isRTL() ? 'left' : 'right';
 473+ }
 474+
 475+
 476+ if ( $width === false ) {
 477+ $wopt = $wgUser->getOption( 'thumbsize' );
 478+
 479+ if( !isset( $wgThumbLimits[$wopt] ) ) {
 480+ $wopt = User::getDefaultOption( 'thumbsize' );
 481+ }
 482+
 483+ $width = min( $img->getWidth(), $wgThumbLimits[$wopt] );
 484+ }
 485+
 486+ return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $width, $height, $framed, $manual_thumb ).$postfix;
 487+ }
 488+
 489+ if ( $width && $img->exists() ) {
 490+
 491+ # Create a resized image, without the additional thumbnail
 492+ # features
 493+
 494+ if ( $height == false )
 495+ $height = -1;
 496+ if ( $manual_thumb == '') {
 497+ $thumb = $img->getThumbnail( $width, $height, $wgGenerateThumbnailOnParse );
 498+ if ( $thumb ) {
 499+ // In most cases, $width = $thumb->width or $height = $thumb->height.
 500+ // If not, we're scaling the image larger than it can be scaled,
 501+ // so we send to the browser a smaller thumbnail, and let the client do the scaling.
 502+
 503+ if ($height != -1 && $width > $thumb->width * $height / $thumb->height) {
 504+ // $height is the limiting factor, not $width
 505+ // set $width to the largest it can be, such that the resulting
 506+ // scaled height is at most $height
 507+ $width = floor($thumb->width * $height / $thumb->height);
 508+ }
 509+ $height = round($thumb->height * $width / $thumb->width);
 510+
 511+ wfDebug( "makeImageLinkObj: client-size set to '$width x $height'\n" );
 512+ $url = $thumb->getUrl();
 513+ } else {
 514+ $error = htmlspecialchars( $img->getLastError() );
 515+ // Do client-side scaling...
 516+ $height = intval( $img->getHeight() * $width / $img->getWidth() );
 517+ }
 518+ }
 519+ } else {
 520+ $width = $img->width;
 521+ $height = $img->height;
 522+ }
 523+
 524+ wfDebug( "makeImageLinkObj2: '$width'x'$height'\n" );
 525+ $u = $nt->escapeLocalURL();
 526+ if ( $error ) {
 527+ $s = $error;
 528+ } elseif ( $url == '' ) {
 529+ $s = $this->makeBrokenImageLinkObj( $img->getTitle() );
 530+ //$s .= "<br />{$alt}<br />{$url}<br />\n";
 531+ } else {
 532+ $s = '<a href="'.$u.'" class="image" title="'.$alt.'">' .
 533+ '<img src="'.$url.'" alt="'.$alt.'" ' .
 534+ ( $width
 535+ ? ( 'width="'.$width.'" height="'.$height.'" ' )
 536+ : '' ) .
 537+ 'longdesc="'.$u.'" /></a>';
 538+ }
 539+ if ( '' != $align ) {
 540+ $s = "<div class=\"float{$align}\"><span>{$s}</span></div>";
 541+ }
 542+ return str_replace("\n", ' ',$prefix.$s.$postfix);
 543+ }
 544+
 545+ /**
 546+ * Make HTML for a thumbnail including image, border and caption
 547+ * $img is an Image object
 548+ */
 549+ function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $boxwidth = 180, $boxheight=false, $framed=false , $manual_thumb = "" ) {
 550+ global $wgStylePath, $wgContLang, $wgGenerateThumbnailOnParse;
 551+ $thumbUrl = '';
 552+ $error = '';
 553+
 554+ $width = $height = 0;
 555+ if ( $img->exists() ) {
 556+ $width = $img->getWidth();
 557+ $height = $img->getHeight();
 558+ }
 559+ if ( 0 == $width || 0 == $height ) {
 560+ $width = $height = 180;
 561+ }
 562+ if ( $boxwidth == 0 ) {
 563+ $boxwidth = 180;
 564+ }
 565+ if ( $framed ) {
 566+ // Use image dimensions, don't scale
 567+ $boxwidth = $width;
 568+ $boxheight = $height;
 569+ $thumbUrl = $img->getViewURL();
 570+ } else {
 571+ if ( $boxheight === false )
 572+ $boxheight = -1;
 573+ if ( '' == $manual_thumb ) {
 574+ $thumb = $img->getThumbnail( $boxwidth, $boxheight, $wgGenerateThumbnailOnParse );
 575+ if ( $thumb ) {
 576+ $thumbUrl = $thumb->getUrl();
 577+ $boxwidth = $thumb->width;
 578+ $boxheight = $thumb->height;
 579+ } else {
 580+ $error = $img->getLastError();
 581+ }
 582+ }
 583+ }
 584+ $oboxwidth = $boxwidth + 2;
 585+
 586+ if ( $manual_thumb != '' ) # Use manually specified thumbnail
 587+ {
 588+ $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb ); #new Title ( $manual_thumb ) ;
 589+ if( $manual_title ) {
 590+ $manual_img = new Image( $manual_title );
 591+ $thumbUrl = $manual_img->getViewURL();
 592+ if ( $manual_img->exists() )
 593+ {
 594+ $width = $manual_img->getWidth();
 595+ $height = $manual_img->getHeight();
 596+ $boxwidth = $width ;
 597+ $boxheight = $height ;
 598+ $oboxwidth = $boxwidth + 2 ;
 599+ }
 600+ }
 601+ }
 602+
 603+ $u = $img->getEscapeLocalURL();
 604+
 605+ $more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
 606+ $magnifyalign = $wgContLang->isRTL() ? 'left' : 'right';
 607+ $textalign = $wgContLang->isRTL() ? ' style="text-align:right"' : '';
 608+
 609+ $s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$oboxwidth}px;\">";
 610+ if( $thumbUrl == '' ) {
 611+ // Couldn't generate thumbnail? Scale the image client-side.
 612+ $thumbUrl = $img->getViewURL();
 613+ if( $boxheight == -1 ) {
 614+ // Approximate...
 615+ $boxheight = intval( $height * $boxwidth / $width );
 616+ }
 617+ }
 618+ if ( $error ) {
 619+ $s .= htmlspecialchars( $error );
 620+ $zoomicon = '';
 621+ } elseif( !$img->exists() ) {
 622+ $s .= $this->makeBrokenImageLinkObj( $img->getTitle() );
 623+ $zoomicon = '';
 624+ } else {
 625+ $s .= '<a href="'.$u.'" class="internal" title="'.$alt.'">'.
 626+ '<img src="'.$thumbUrl.'" alt="'.$alt.'" ' .
 627+ 'width="'.$boxwidth.'" height="'.$boxheight.'" ' .
 628+ 'longdesc="'.$u.'" class="thumbimage" /></a>';
 629+ if ( $framed ) {
 630+ $zoomicon="";
 631+ } else {
 632+ $zoomicon = '<div class="magnify" style="float:'.$magnifyalign.'">'.
 633+ '<a href="'.$u.'" class="internal" title="'.$more.'">'.
 634+ '<img src="'.$wgStylePath.'/common/images/magnify-clip.png" ' .
 635+ 'width="15" height="11" alt="" /></a></div>';
 636+ }
 637+ }
 638+ $s .= ' <div class="thumbcaption"'.$textalign.'>'.$zoomicon.$label."</div></div></div>";
 639+ return str_replace("\n", ' ', $s);
 640+ }
 641+
 642+ /**
 643+ * Pass a title object, not a title string
 644+ */
 645+ function makeBrokenImageLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 646+ # Fail gracefully
 647+ if ( ! isset($nt) ) {
 648+ # throw new MWException();
 649+ return "<!-- ERROR -->{$prefix}{$text}{$trail}";
 650+ }
 651+
 652+ $fname = 'Linker::makeBrokenImageLinkObj';
 653+ wfProfileIn( $fname );
 654+
 655+ $q = 'wpDestFile=' . urlencode( $nt->getDBkey() );
 656+ if ( '' != $query ) {
 657+ $q .= "&$query";
 658+ }
 659+ $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
 660+ $url = $uploadTitle->escapeLocalURL( $q );
 661+
 662+ if ( '' == $text ) {
 663+ $text = htmlspecialchars( $nt->getPrefixedText() );
 664+ }
 665+ $style = $this->getInternalLinkAttributesObj( $nt, $text, "yes" );
 666+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 667+ $s = "<a href=\"{$url}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
 668+
 669+ wfProfileOut( $fname );
 670+ return $s;
 671+ }
 672+
 673+ /** @todo document */
 674+ function makeMediaLink( $name, /* wtf?! */ $url, $alt = '' ) {
 675+ $nt = Title::makeTitleSafe( NS_IMAGE, $name );
 676+ return $this->makeMediaLinkObj( $nt, $alt );
 677+ }
 678+
 679+ /**
 680+ * Create a direct link to a given uploaded file.
 681+ *
 682+ * @param $title Title object.
 683+ * @param $text String: pre-sanitized HTML
 684+ * @param $nourl Boolean: Mask absolute URLs, so the parser doesn't
 685+ * linkify them (it is currently not context-aware)
 686+ * @return string HTML
 687+ *
 688+ * @public
 689+ * @todo Handle invalid or missing images better.
 690+ */
 691+ function makeMediaLinkObj( $title, $text = '' ) {
 692+ if( is_null( $title ) ) {
 693+ ### HOTFIX. Instead of breaking, return empty string.
 694+ return $text;
 695+ } else {
 696+ $img = new Image( $title );
 697+ if( $img->exists() ) {
 698+ $url = $img->getURL();
 699+ $class = 'internal';
 700+ } else {
 701+ $upload = SpecialPage::getTitleFor( 'Upload' );
 702+ $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $img->getName() ) );
 703+ $class = 'new';
 704+ }
 705+ $alt = htmlspecialchars( $title->getText() );
 706+ if( $text == '' ) {
 707+ $text = $alt;
 708+ }
 709+ $u = htmlspecialchars( $url );
 710+ return "<a href=\"{$u}\" class=\"$class\" title=\"{$alt}\">{$text}</a>";
 711+ }
 712+ }
 713+
 714+ /** @todo document */
 715+ function specialLink( $name, $key = '' ) {
 716+ global $wgContLang;
 717+
 718+ if ( '' == $key ) { $key = strtolower( $name ); }
 719+ $pn = $wgContLang->ucfirst( $name );
 720+ return $this->makeKnownLink( $wgContLang->specialPage( $pn ),
 721+ wfMsg( $key ) );
 722+ }
 723+
 724+ /** @todo document */
 725+ function makeExternalLink( $url, $text, $escape = true, $linktype = '', $ns = null ) {
 726+ $style = $this->getExternalLinkAttributes( $url, $text, 'external ' . $linktype );
 727+ global $wgNoFollowLinks, $wgNoFollowNsExceptions;
 728+ if( $wgNoFollowLinks && !(isset($ns) && in_array($ns, $wgNoFollowNsExceptions)) ) {
 729+ $style .= ' rel="nofollow"';
 730+ }
 731+ $url = htmlspecialchars( $url );
 732+ if( $escape ) {
 733+ $text = htmlspecialchars( $text );
 734+ }
 735+ return '<a href="'.$url.'"'.$style.'>'.$text.'</a>';
 736+ }
 737+
 738+ /**
 739+ * Make user link (or user contributions for unregistered users)
 740+ * @param $userId Integer: user id in database.
 741+ * @param $userText String: user name in database
 742+ * @return string HTML fragment
 743+ * @private
 744+ */
 745+ function userLink( $userId, $userText ) {
 746+ $encName = htmlspecialchars( $userText );
 747+ if( $userId == 0 ) {
 748+ $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
 749+ return $this->makeKnownLinkObj( $contribsPage,
 750+ $encName);
 751+ } else {
 752+ $userPage = Title::makeTitle( NS_USER, $userText );
 753+ return $this->makeLinkObj( $userPage, $encName );
 754+ }
 755+ }
 756+
 757+ /**
 758+ * @param $userId Integer: user id in database.
 759+ * @param $userText String: user name in database.
 760+ * @return string HTML fragment with talk and/or block links
 761+ * @private
 762+ */
 763+ function userToolLinks( $userId, $userText ) {
 764+ global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
 765+ $talkable = !( $wgDisableAnonTalk && 0 == $userId );
 766+ $blockable = ( $wgSysopUserBans || 0 == $userId );
 767+
 768+ $items = array();
 769+ if( $talkable ) {
 770+ $items[] = $this->userTalkLink( $userId, $userText );
 771+ }
 772+ if( $userId ) {
 773+ $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
 774+ $items[] = $this->makeKnownLinkObj( $contribsPage ,
 775+ wfMsgHtml( 'contribslink' ) );
 776+ }
 777+ if( $blockable && $wgUser->isAllowed( 'block' ) ) {
 778+ $items[] = $this->blockLink( $userId, $userText );
 779+ }
 780+
 781+ if( $items ) {
 782+ return ' (' . implode( ' | ', $items ) . ')';
 783+ } else {
 784+ return '';
 785+ }
 786+ }
 787+
 788+ /**
 789+ * @param $userId Integer: user id in database.
 790+ * @param $userText String: user name in database.
 791+ * @return string HTML fragment with user talk link
 792+ * @private
 793+ */
 794+ function userTalkLink( $userId, $userText ) {
 795+ global $wgLang;
 796+ $talkname = $wgLang->getNsText( NS_TALK ); # use the shorter name
 797+
 798+ $userTalkPage = Title::makeTitle( NS_USER_TALK, $userText );
 799+ $userTalkLink = $this->makeLinkObj( $userTalkPage, $talkname );
 800+ return $userTalkLink;
 801+ }
 802+
 803+ /**
 804+ * @param $userId Integer: userid
 805+ * @param $userText String: user name in database.
 806+ * @return string HTML fragment with block link
 807+ * @private
 808+ */
 809+ function blockLink( $userId, $userText ) {
 810+ $blockPage = SpecialPage::getTitleFor( 'Blockip', $userText );
 811+ $blockLink = $this->makeKnownLinkObj( $blockPage,
 812+ wfMsgHtml( 'blocklink' ) );
 813+ return $blockLink;
 814+ }
 815+
 816+ /**
 817+ * Generate a user link if the current user is allowed to view it
 818+ * @param $rev Revision object.
 819+ * @return string HTML
 820+ */
 821+ function revUserLink( $rev ) {
 822+ if( $rev->userCan( Revision::DELETED_USER ) ) {
 823+ $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() );
 824+ } else {
 825+ $link = wfMsgHtml( 'rev-deleted-user' );
 826+ }
 827+ if( $rev->isDeleted( Revision::DELETED_USER ) ) {
 828+ return '<span class="history-deleted">' . $link . '</span>';
 829+ }
 830+ return $link;
 831+ }
 832+
 833+ /**
 834+ * Generate a user tool link cluster if the current user is allowed to view it
 835+ * @param $rev Revision object.
 836+ * @return string HTML
 837+ */
 838+ function revUserTools( $rev ) {
 839+ if( $rev->userCan( Revision::DELETED_USER ) ) {
 840+ $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ) .
 841+ ' ' .
 842+ $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() );
 843+ } else {
 844+ $link = wfMsgHtml( 'rev-deleted-user' );
 845+ }
 846+ if( $rev->isDeleted( Revision::DELETED_USER ) ) {
 847+ return '<span class="history-deleted">' . $link . '</span>';
 848+ }
 849+ return $link;
 850+ }
 851+
 852+ /**
 853+ * This function is called by all recent changes variants, by the page history,
 854+ * and by the user contributions list. It is responsible for formatting edit
 855+ * comments. It escapes any HTML in the comment, but adds some CSS to format
 856+ * auto-generated comments (from section editing) and formats [[wikilinks]].
 857+ *
 858+ * @author Erik Moeller <moeller@scireview.de>
 859+ *
 860+ * Note: there's not always a title to pass to this function.
 861+ * Since you can't set a default parameter for a reference, I've turned it
 862+ * temporarily to a value pass. Should be adjusted further. --brion
 863+ *
 864+ * $param string $comment
 865+ * @param mixed $title Title object (to generate link to the section in autocomment) or null
 866+ * @param bool $local Whether section links should refer to local page
 867+ */
 868+ function formatComment($comment, $title = NULL, $local = false) {
 869+ wfProfileIn( __METHOD__ );
 870+
 871+ global $wgContLang;
 872+ $comment = str_replace( "\n", " ", $comment );
 873+ $comment = htmlspecialchars( $comment );
 874+
 875+ # The pattern for autogen comments is / * foo * /, which makes for
 876+ # some nasty regex.
 877+ # We look for all comments, match any text before and after the comment,
 878+ # add a separator where needed and format the comment itself with CSS
 879+ $match = array();
 880+ while (preg_match('/(.*)\/\*\s*(.*?)\s*\*\/(.*)/', $comment,$match)) {
 881+ $pre=$match[1];
 882+ $auto=$match[2];
 883+ $post=$match[3];
 884+ $link='';
 885+ if( $title ) {
 886+ $section = $auto;
 887+
 888+ # Generate a valid anchor name from the section title.
 889+ # Hackish, but should generally work - we strip wiki
 890+ # syntax, including the magic [[: that is used to
 891+ # "link rather than show" in case of images and
 892+ # interlanguage links.
 893+ $section = str_replace( '[[:', '', $section );
 894+ $section = str_replace( '[[', '', $section );
 895+ $section = str_replace( ']]', '', $section );
 896+ if ( $local ) {
 897+ $sectionTitle = Title::newFromText( '#' . $section);
 898+ } else {
 899+ $sectionTitle = wfClone( $title );
 900+ $sectionTitle->mFragment = $section;
 901+ }
 902+ $link = $this->makeKnownLinkObj( $sectionTitle, wfMsg( 'sectionlink' ) );
 903+ }
 904+ $sep='-';
 905+ $auto=$link.$auto;
 906+ if($pre) { $auto = $sep.' '.$auto; }
 907+ if($post) { $auto .= ' '.$sep; }
 908+ $auto='<span class="autocomment">'.$auto.'</span>';
 909+ $comment=$pre.$auto.$post;
 910+ }
 911+
 912+ # format regular and media links - all other wiki formatting
 913+ # is ignored
 914+ $medians = '(?:' . preg_quote( Namespace::getCanonicalName( NS_MEDIA ), '/' ) . '|';
 915+ $medians .= preg_quote( $wgContLang->getNsText( NS_MEDIA ), '/' ) . '):';
 916+ while(preg_match('/\[\[:?(.*?)(\|(.*?))*\]\](.*)$/',$comment,$match)) {
 917+ # Handle link renaming [[foo|text]] will show link as "text"
 918+ if( "" != $match[3] ) {
 919+ $text = $match[3];
 920+ } else {
 921+ $text = $match[1];
 922+ }
 923+ $submatch = array();
 924+ if( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) {
 925+ # Media link; trail not supported.
 926+ $linkRegexp = '/\[\[(.*?)\]\]/';
 927+ $thelink = $this->makeMediaLink( $submatch[1], "", $text );
 928+ } else {
 929+ # Other kind of link
 930+ if( preg_match( $wgContLang->linkTrail(), $match[4], $submatch ) ) {
 931+ $trail = $submatch[1];
 932+ } else {
 933+ $trail = "";
 934+ }
 935+ $linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/';
 936+ if (isset($match[1][0]) && $match[1][0] == ':')
 937+ $match[1] = substr($match[1], 1);
 938+ $thelink = $this->makeLink( $match[1], $text, "", $trail );
 939+ }
 940+ $comment = preg_replace( $linkRegexp, StringUtils::escapeRegexReplacement( $thelink ), $comment, 1 );
 941+ }
 942+ wfProfileOut( __METHOD__ );
 943+ return $comment;
 944+ }
 945+
 946+ /**
 947+ * Wrap a comment in standard punctuation and formatting if
 948+ * it's non-empty, otherwise return empty string.
 949+ *
 950+ * @param string $comment
 951+ * @param mixed $title Title object (to generate link to section in autocomment) or null
 952+ * @param bool $local Whether section links should refer to local page
 953+ *
 954+ * @return string
 955+ */
 956+ function commentBlock( $comment, $title = NULL, $local = false ) {
 957+ // '*' used to be the comment inserted by the software way back
 958+ // in antiquity in case none was provided, here for backwards
 959+ // compatability, acc. to brion -ævar
 960+ if( $comment == '' || $comment == '*' ) {
 961+ return '';
 962+ } else {
 963+ $formatted = $this->formatComment( $comment, $title, $local );
 964+ return " <span class=\"comment\">($formatted)</span>";
 965+ }
 966+ }
 967+
 968+ /**
 969+ * Wrap and format the given revision's comment block, if the current
 970+ * user is allowed to view it.
 971+ *
 972+ * @param Revision $rev
 973+ * @param bool $local Whether section links should refer to local page
 974+ * @return string HTML
 975+ */
 976+ function revComment( Revision $rev, $local = false ) {
 977+ if( $rev->userCan( Revision::DELETED_COMMENT ) ) {
 978+ $block = $this->commentBlock( $rev->getRawComment(), $rev->getTitle(), $local );
 979+ } else {
 980+ $block = " <span class=\"comment\">" .
 981+ wfMsgHtml( 'rev-deleted-comment' ) . "</span>";
 982+ }
 983+ if( $rev->isDeleted( Revision::DELETED_COMMENT ) ) {
 984+ return " <span class=\"history-deleted\">$block</span>";
 985+ }
 986+ return $block;
 987+ }
 988+
 989+ /** @todo document */
 990+ function tocIndent() {
 991+ return "\n<ul>";
 992+ }
 993+
 994+ /** @todo document */
 995+ function tocUnindent($level) {
 996+ return "</li>\n" . str_repeat( "</ul>\n</li>\n", $level>0 ? $level : 0 );
 997+ }
 998+
 999+ /**
 1000+ * parameter level defines if we are on an indentation level
 1001+ */
 1002+ function tocLine( $anchor, $tocline, $tocnumber, $level ) {
 1003+ return "\n<li class=\"toclevel-$level\"><a href=\"#" .
 1004+ $anchor . '"><span class="tocnumber">' .
 1005+ $tocnumber . '</span> <span class="toctext">' .
 1006+ $tocline . '</span></a>';
 1007+ }
 1008+
 1009+ /** @todo document */
 1010+ function tocLineEnd() {
 1011+ return "</li>\n";
 1012+ }
 1013+
 1014+ /** @todo document */
 1015+ function tocList($toc) {
 1016+ global $wgJsMimeType;
 1017+ $title = wfMsgForContent('toc') ;
 1018+ return
 1019+ '<table id="toc" class="toc" summary="' . $title .'"><tr><td>'
 1020+ . '<div id="toctitle"><h2>' . $title . "</h2></div>\n"
 1021+ . $toc
 1022+ # no trailing newline, script should not be wrapped in a
 1023+ # paragraph
 1024+ . "</ul>\n</td></tr></table>"
 1025+ . '<script type="' . $wgJsMimeType . '">'
 1026+ . ' if (window.showTocToggle) {'
 1027+ . ' var tocShowText = "' . wfEscapeJsString( wfMsgForContent('showtoc') ) . '";'
 1028+ . ' var tocHideText = "' . wfEscapeJsString( wfMsgForContent('hidetoc') ) . '";'
 1029+ . ' showTocToggle();'
 1030+ . ' } '
 1031+ . "</script>\n";
 1032+ }
 1033+
 1034+ /** @todo document */
 1035+ public function editSectionLinkForOther( $title, $section ) {
 1036+ global $wgContLang;
 1037+
 1038+ $title = Title::newFromText( $title );
 1039+ $editurl = '&section='.$section;
 1040+ $url = $this->makeKnownLinkObj( $title, wfMsg('editsection'), 'action=edit'.$editurl );
 1041+
 1042+### - return "<span class=\"editsection\">[".$url."]</span>";
 1043+ /** Added editSectionLinkForOther hook that allows section/header link to be modified */
 1044+ $result = null;
 1045+ wfRunHooks( 'EditSectionLinkForOther', array( &$this, $title, $section, $url, &$result ) );
 1046+ return is_null( $result )
 1047+ ? "<span class=\"editsection\">[{$url}]</span>"
 1048+ : "<span class=\"editsection\">[{$result}]</span>";
 1049+ /** End of hook */
 1050+ }
 1051+
 1052+ /**
 1053+ * @param $title Title object.
 1054+ * @param $section Integer: section number.
 1055+ * @param $hint Link String: title, or default if omitted or empty
 1056+ */
 1057+ public function editSectionLink( $nt, $section, $hint='' ) {
 1058+ global $wgContLang;
 1059+
 1060+ $editurl = '&section='.$section;
 1061+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) ) . '"';
 1062+ $url = $this->makeKnownLinkObj( $nt, wfMsg('editsection'), 'action=edit'.$editurl, '', '', '', $hint );
 1063+
 1064+### - return "<span class=\"editsection\">[".$url."]</span>";
 1065+ /** Added editSectionLink hook that allows section/header link to be modified */
 1066+ $result = null;
 1067+ wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $hint, $url, &$result ) );
 1068+ return is_null( $result )
 1069+ ? "<span class=\"editsection\">[{$url}]</span>"
 1070+ : "<span class=\"editsection\">[{$result}]</span>";
 1071+ /** End of hook */
 1072+ }
 1073+
 1074+ /**
 1075+ * Create a headline for content
 1076+ *
 1077+ * @param int $level The level of the headline (1-6)
 1078+ * @param string $attribs Any attributes for the headline, starting with a space and ending with '>'
 1079+ * This *must* be at least '>' for no attribs
 1080+ * @param string $anchor The anchor to give the headline (the bit after the #)
 1081+ * @param string $text The text of the header
 1082+ * @param string $link HTML to add for the section edit link
 1083+ *
 1084+ * @return string HTML headline
 1085+ */
 1086+ public function makeHeadline( $level, $attribs, $anchor, $text, $link ) {
 1087+ return "<a name=\"$anchor\"></a><h$level$attribs$link <span class=\"mw-headline\">$text</span></h$level>";
 1088+ }
 1089+
 1090+ /**
 1091+ * Split a link trail, return the "inside" portion and the remainder of the trail
 1092+ * as a two-element array
 1093+ *
 1094+ * @static
 1095+ */
 1096+ static function splitTrail( $trail ) {
 1097+ static $regex = false;
 1098+ if ( $regex === false ) {
 1099+ global $wgContLang;
 1100+ $regex = $wgContLang->linkTrail();
 1101+ }
 1102+ $inside = '';
 1103+ if ( '' != $trail ) {
 1104+ $m = array();
 1105+ if ( preg_match( $regex, $trail, $m ) ) {
 1106+ $inside = $m[1];
 1107+ $trail = $m[2];
 1108+ }
 1109+ }
 1110+ return array( $inside, $trail );
 1111+ }
 1112+
 1113+ /**
 1114+ * Generate a rollback link for a given revision. Currently it's the
 1115+ * caller's responsibility to ensure that the revision is the top one. If
 1116+ * it's not, of course, the user will get an error message.
 1117+ *
 1118+ * If the calling page is called with the parameter &bot=1, all rollback
 1119+ * links also get that parameter. It causes the edit itself and the rollback
 1120+ * to be marked as "bot" edits. Bot edits are hidden by default from recent
 1121+ * changes, so this allows sysops to combat a busy vandal without bothering
 1122+ * other users.
 1123+ *
 1124+ * @param Revision $rev
 1125+ */
 1126+ function generateRollback( $rev ) {
 1127+ global $wgUser, $wgRequest;
 1128+ $title = $rev->getTitle();
 1129+
 1130+ $extraRollback = $wgRequest->getBool( 'bot' ) ? '&bot=1' : '';
 1131+ $extraRollback .= '&token=' . urlencode(
 1132+ $wgUser->editToken( array( $title->getPrefixedText(), $rev->getUserText() ) ) );
 1133+ return '<span class="mw-rollback-link">['. $this->makeKnownLinkObj( $title,
 1134+ wfMsg('rollbacklink'),
 1135+ 'action=rollback&from=' . urlencode( $rev->getUserText() ) . $extraRollback ) .']</span>';
 1136+ }
 1137+
 1138+ /**
 1139+ * Returns HTML for the "templates used on this page" list.
 1140+ *
 1141+ * @param array $templates Array of templates from Article::getUsedTemplate
 1142+ * or similar
 1143+ * @param bool $preview Whether this is for a preview
 1144+ * @param bool $section Whether this is for a section edit
 1145+ * @return string HTML output
 1146+ */
 1147+ public function formatTemplates( $templates, $preview = false, $section = false) {
 1148+ global $wgUser;
 1149+ wfProfileIn( __METHOD__ );
 1150+
 1151+ $sk =& $wgUser->getSkin();
 1152+
 1153+ $outText = '';
 1154+ if ( count( $templates ) > 0 ) {
 1155+ # Do a batch existence check
 1156+ $batch = new LinkBatch;
 1157+ foreach( $templates as $title ) {
 1158+ $batch->addObj( $title );
 1159+ }
 1160+ $batch->execute();
 1161+
 1162+ # Construct the HTML
 1163+ $outText = '<div class="mw-templatesUsedExplanation">';
 1164+ if ( $preview ) {
 1165+ $outText .= wfMsgExt( 'templatesusedpreview', array( 'parse' ) );
 1166+ } elseif ( $section ) {
 1167+ $outText .= wfMsgExt( 'templatesusedsection', array( 'parse' ) );
 1168+ } else {
 1169+ $outText .= wfMsgExt( 'templatesused', array( 'parse' ) );
 1170+ }
 1171+ $outText .= '</div><ul>';
 1172+
 1173+ foreach ( $templates as $titleObj ) {
 1174+ $r = $titleObj->getRestrictions( 'edit' );
 1175+ if ( in_array( 'sysop', $r ) ) {
 1176+ $protected = wfMsgExt( 'template-protected', array( 'parseinline' ) );
 1177+ } elseif ( in_array( 'autoconfirmed', $r ) ) {
 1178+ $protected = wfMsgExt( 'template-semiprotected', array( 'parseinline' ) );
 1179+ } else {
 1180+ $protected = '';
 1181+ }
 1182+ $outText .= '<li>' . $sk->makeLinkObj( $titleObj ) . ' ' . $protected . '</li>';
 1183+ }
 1184+ $outText .= '</ul>';
 1185+ }
 1186+ wfProfileOut( __METHOD__ );
 1187+ return $outText;
 1188+ }
 1189+
 1190+ /**
 1191+ * Format a size in bytes for output, using an appropriate
 1192+ * unit (B, KB, MB or GB) according to the magnitude in question
 1193+ *
 1194+ * @param $size Size to format
 1195+ * @return string
 1196+ */
 1197+ public function formatSize( $size ) {
 1198+ global $wgLang;
 1199+ if( $size > 1024 ) {
 1200+ $size = $size / 1024;
 1201+ if( $size > 1024 ) {
 1202+ $size = $size / 1024;
 1203+ if( $size > 1024 ) {
 1204+ $size = $size / 1024;
 1205+ $msg = 'size-gigabytes';
 1206+ } else {
 1207+ $msg = 'size-megabytes';
 1208+ }
 1209+ } else {
 1210+ $msg = 'size-kilobytes';
 1211+ }
 1212+ } else {
 1213+ $msg = 'size-bytes';
 1214+ }
 1215+ $size = round( $size, 0 );
 1216+ return wfMsgHtml( $msg, $wgLang->formatNum( $size ) );
 1217+ }
 1218+
 1219+}
 1220+
 1221+?>
Index: trunk/extensions/DiscussionThreading/REV1_10_0/DiscussionThreading1_10.patch
@@ -0,0 +1,37 @@
 2+Index: Linker.php
 3+===================================================================
 4+--- Linker.php (revision 22908)
 5+@@ -989,8 +989,14 @@
 6+ $editurl = '&section='.$section;
 7+ $url = $this->makeKnownLinkObj( $title, wfMsg('editsection'), 'action=edit'.$editurl );
 8+
 9+- return "<span class=\"editsection\">[".$url."]</span>";
 10+-
 11++### - return "<span class=\"editsection\">[".$url."]</span>";
 12++ /** Added editSectionLinkForOther hook that allows section/header link to be modified */
 13++ $result = null;
 14++ wfRunHooks( 'EditSectionLinkForOther', array( &$this, $title, $section, $url, &$result ) );
 15++ return is_null( $result )
 16++ ? "<span class=\"editsection\">[{$url}]</span>"
 17++ : "<span class=\"editsection\">[{$result}]</span>";
 18++ /** End of hook */
 19+ }
 20+
 21+ /**
 22+@@ -1005,7 +1011,14 @@
 23+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) ) . '"';
 24+ $url = $this->makeKnownLinkObj( $nt, wfMsg('editsection'), 'action=edit'.$editurl, '', '', '', $hint );
 25+
 26+- return "<span class=\"editsection\">[".$url."]</span>";
 27++### - return "<span class=\"editsection\">[".$url."]</span>";
 28++ /** Added editSectionLink hook that allows section/header link to be modified */
 29++ $result = null;
 30++ wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $hint, $url, &$result ) );
 31++ return is_null( $result )
 32++ ? "<span class=\"editsection\">[{$url}]</span>"
 33++ : "<span class=\"editsection\">[{$result}]</span>";
 34++ /** End of hook */
 35+ }
 36+
 37+ /**
Index: trunk/extensions/DiscussionThreading/REV1_10_0/includes/Linker.php
@@ -0,0 +1,1224 @@
 2+<?php
 3+/**
 4+ * Split off some of the internal bits from Skin.php.
 5+ * These functions are used for primarily page content:
 6+ * links, embedded images, table of contents. Links are
 7+ * also used in the skin.
 8+ * For the moment, Skin is a descendent class of Linker.
 9+ * In the future, it should probably be further split
 10+ * so that ever other bit of the wiki doesn't have to
 11+ * go loading up Skin to get at it.
 12+ *
 13+ * @addtogroup Skins
 14+ */
 15+class Linker {
 16+ function __construct() {}
 17+
 18+ /**
 19+ * @deprecated
 20+ */
 21+ function postParseLinkColour( $s = NULL ) {
 22+ return NULL;
 23+ }
 24+
 25+ /** @todo document */
 26+ function getExternalLinkAttributes( $link, $text, $class='' ) {
 27+ $link = htmlspecialchars( $link );
 28+
 29+ $r = ($class != '') ? " class=\"$class\"" : " class=\"external\"";
 30+
 31+ $r .= " title=\"{$link}\"";
 32+ return $r;
 33+ }
 34+
 35+ function getInterwikiLinkAttributes( $link, $text, $class='' ) {
 36+ global $wgContLang;
 37+
 38+ $link = urldecode( $link );
 39+ $link = $wgContLang->checkTitleEncoding( $link );
 40+ $link = preg_replace( '/[\\x00-\\x1f]/', ' ', $link );
 41+ $link = htmlspecialchars( $link );
 42+
 43+ $r = ($class != '') ? " class=\"$class\"" : " class=\"external\"";
 44+
 45+ $r .= " title=\"{$link}\"";
 46+ return $r;
 47+ }
 48+
 49+ /** @todo document */
 50+ function getInternalLinkAttributes( $link, $text, $broken = false ) {
 51+ $link = urldecode( $link );
 52+ $link = str_replace( '_', ' ', $link );
 53+ $link = htmlspecialchars( $link );
 54+
 55+ if( $broken == 'stub' ) {
 56+ $r = ' class="stub"';
 57+ } else if ( $broken == 'yes' ) {
 58+ $r = ' class="new"';
 59+ } else {
 60+ $r = '';
 61+ }
 62+
 63+ $r .= " title=\"{$link}\"";
 64+ return $r;
 65+ }
 66+
 67+ /**
 68+ * @param $nt Title object.
 69+ * @param $text String: FIXME
 70+ * @param $broken Boolean: FIXME, default 'false'.
 71+ */
 72+ function getInternalLinkAttributesObj( &$nt, $text, $broken = false ) {
 73+ if( $broken == 'stub' ) {
 74+ $r = ' class="stub"';
 75+ } else if ( $broken == 'yes' ) {
 76+ $r = ' class="new"';
 77+ } else {
 78+ $r = '';
 79+ }
 80+
 81+ $r .= ' title="' . $nt->getEscapedText() . '"';
 82+ return $r;
 83+ }
 84+
 85+ /**
 86+ * This function is a shortcut to makeLinkObj(Title::newFromText($title),...). Do not call
 87+ * it if you already have a title object handy. See makeLinkObj for further documentation.
 88+ *
 89+ * @param $title String: the text of the title
 90+ * @param $text String: link text
 91+ * @param $query String: optional query part
 92+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 93+ * be included in the link text. Other characters will be appended after
 94+ * the end of the link.
 95+ */
 96+ function makeLink( $title, $text = '', $query = '', $trail = '' ) {
 97+ wfProfileIn( 'Linker::makeLink' );
 98+ $nt = Title::newFromText( $title );
 99+ if ($nt) {
 100+ $result = $this->makeLinkObj( Title::newFromText( $title ), $text, $query, $trail );
 101+ } else {
 102+ wfDebug( 'Invalid title passed to Linker::makeLink(): "'.$title."\"\n" );
 103+ $result = $text == "" ? $title : $text;
 104+ }
 105+
 106+ wfProfileOut( 'Linker::makeLink' );
 107+ return $result;
 108+ }
 109+
 110+ /**
 111+ * This function is a shortcut to makeKnownLinkObj(Title::newFromText($title),...). Do not call
 112+ * it if you already have a title object handy. See makeKnownLinkObj for further documentation.
 113+ *
 114+ * @param $title String: the text of the title
 115+ * @param $text String: link text
 116+ * @param $query String: optional query part
 117+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 118+ * be included in the link text. Other characters will be appended after
 119+ * the end of the link.
 120+ */
 121+ function makeKnownLink( $title, $text = '', $query = '', $trail = '', $prefix = '',$aprops = '') {
 122+ $nt = Title::newFromText( $title );
 123+ if ($nt) {
 124+ return $this->makeKnownLinkObj( Title::newFromText( $title ), $text, $query, $trail, $prefix , $aprops );
 125+ } else {
 126+ wfDebug( 'Invalid title passed to Linker::makeKnownLink(): "'.$title."\"\n" );
 127+ return $text == '' ? $title : $text;
 128+ }
 129+ }
 130+
 131+ /**
 132+ * This function is a shortcut to makeBrokenLinkObj(Title::newFromText($title),...). Do not call
 133+ * it if you already have a title object handy. See makeBrokenLinkObj for further documentation.
 134+ *
 135+ * @param string $title The text of the title
 136+ * @param string $text Link text
 137+ * @param string $query Optional query part
 138+ * @param string $trail Optional trail. Alphabetic characters at the start of this string will
 139+ * be included in the link text. Other characters will be appended after
 140+ * the end of the link.
 141+ */
 142+ function makeBrokenLink( $title, $text = '', $query = '', $trail = '' ) {
 143+ $nt = Title::newFromText( $title );
 144+ if ($nt) {
 145+ return $this->makeBrokenLinkObj( Title::newFromText( $title ), $text, $query, $trail );
 146+ } else {
 147+ wfDebug( 'Invalid title passed to Linker::makeBrokenLink(): "'.$title."\"\n" );
 148+ return $text == '' ? $title : $text;
 149+ }
 150+ }
 151+
 152+ /**
 153+ * This function is a shortcut to makeStubLinkObj(Title::newFromText($title),...). Do not call
 154+ * it if you already have a title object handy. See makeStubLinkObj for further documentation.
 155+ *
 156+ * @param $title String: the text of the title
 157+ * @param $text String: link text
 158+ * @param $query String: optional query part
 159+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 160+ * be included in the link text. Other characters will be appended after
 161+ * the end of the link.
 162+ */
 163+ function makeStubLink( $title, $text = '', $query = '', $trail = '' ) {
 164+ $nt = Title::newFromText( $title );
 165+ if ($nt) {
 166+ return $this->makeStubLinkObj( Title::newFromText( $title ), $text, $query, $trail );
 167+ } else {
 168+ wfDebug( 'Invalid title passed to Linker::makeStubLink(): "'.$title."\"\n" );
 169+ return $text == '' ? $title : $text;
 170+ }
 171+ }
 172+
 173+ /**
 174+ * Make a link for a title which may or may not be in the database. If you need to
 175+ * call this lots of times, pre-fill the link cache with a LinkBatch, otherwise each
 176+ * call to this will result in a DB query.
 177+ *
 178+ * @param $nt Title: the title object to make the link from, e.g. from
 179+ * Title::newFromText.
 180+ * @param $text String: link text
 181+ * @param $query String: optional query part
 182+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 183+ * be included in the link text. Other characters will be appended after
 184+ * the end of the link.
 185+ * @param $prefix String: optional prefix. As trail, only before instead of after.
 186+ */
 187+ function makeLinkObj( $nt, $text= '', $query = '', $trail = '', $prefix = '' ) {
 188+ global $wgUser;
 189+ $fname = 'Linker::makeLinkObj';
 190+ wfProfileIn( $fname );
 191+
 192+ # Fail gracefully
 193+ if ( ! is_object($nt) ) {
 194+ # throw new MWException();
 195+ wfProfileOut( $fname );
 196+ return "<!-- ERROR -->{$prefix}{$text}{$trail}";
 197+ }
 198+
 199+ if ( $nt->isExternal() ) {
 200+ $u = $nt->getFullURL();
 201+ $link = $nt->getPrefixedURL();
 202+ if ( '' == $text ) { $text = $nt->getPrefixedText(); }
 203+ $style = $this->getInterwikiLinkAttributes( $link, $text, 'extiw' );
 204+
 205+ $inside = '';
 206+ if ( '' != $trail ) {
 207+ $m = array();
 208+ if ( preg_match( '/^([a-z]+)(.*)$$/sD', $trail, $m ) ) {
 209+ $inside = $m[1];
 210+ $trail = $m[2];
 211+ }
 212+ }
 213+ $t = "<a href=\"{$u}\"{$style}>{$text}{$inside}</a>";
 214+
 215+ wfProfileOut( $fname );
 216+ return $t;
 217+ } elseif ( $nt->isAlwaysKnown() ) {
 218+ # Image links, special page links and self-links with fragements are always known.
 219+ $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
 220+ } else {
 221+ wfProfileIn( $fname.'-immediate' );
 222+ # Work out link colour immediately
 223+ $aid = $nt->getArticleID() ;
 224+ if ( 0 == $aid ) {
 225+ $retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix );
 226+ } else {
 227+ $threshold = $wgUser->getOption('stubthreshold') ;
 228+ if ( $threshold > 0 ) {
 229+ $dbr = wfGetDB( DB_SLAVE );
 230+ $s = $dbr->selectRow(
 231+ array( 'page' ),
 232+ array( 'page_len',
 233+ 'page_namespace',
 234+ 'page_is_redirect' ),
 235+ array( 'page_id' => $aid ), $fname ) ;
 236+ if ( $s !== false ) {
 237+ $size = $s->page_len;
 238+ if ( $s->page_is_redirect OR $s->page_namespace != NS_MAIN ) {
 239+ $size = $threshold*2 ; # Really big
 240+ }
 241+ } else {
 242+ $size = $threshold*2 ; # Really big
 243+ }
 244+ } else {
 245+ $size = 1 ;
 246+ }
 247+ if ( $size < $threshold ) {
 248+ $retVal = $this->makeStubLinkObj( $nt, $text, $query, $trail, $prefix );
 249+ } else {
 250+ $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
 251+ }
 252+ }
 253+ wfProfileOut( $fname.'-immediate' );
 254+ }
 255+ wfProfileOut( $fname );
 256+ return $retVal;
 257+ }
 258+
 259+ /**
 260+ * Make a link for a title which definitely exists. This is faster than makeLinkObj because
 261+ * it doesn't have to do a database query. It's also valid for interwiki titles and special
 262+ * pages.
 263+ *
 264+ * @param $nt Title object of target page
 265+ * @param $text String: text to replace the title
 266+ * @param $query String: link target
 267+ * @param $trail String: text after link
 268+ * @param $prefix String: text before link text
 269+ * @param $aprops String: extra attributes to the a-element
 270+ * @param $style String: style to apply - if empty, use getInternalLinkAttributesObj instead
 271+ * @return the a-element
 272+ */
 273+ function makeKnownLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) {
 274+
 275+ $fname = 'Linker::makeKnownLinkObj';
 276+ wfProfileIn( $fname );
 277+
 278+ if ( !is_object( $nt ) ) {
 279+ wfProfileOut( $fname );
 280+ return $text;
 281+ }
 282+
 283+ $u = $nt->escapeLocalURL( $query );
 284+ if ( $nt->getFragment() != '' ) {
 285+ if( $nt->getPrefixedDbkey() == '' ) {
 286+ $u = '';
 287+ if ( '' == $text ) {
 288+ $text = htmlspecialchars( $nt->getFragment() );
 289+ }
 290+ }
 291+ $u .= $nt->getFragmentForURL();
 292+ }
 293+ if ( $text == '' ) {
 294+ $text = htmlspecialchars( $nt->getPrefixedText() );
 295+ }
 296+ if ( $style == '' ) {
 297+ $style = $this->getInternalLinkAttributesObj( $nt, $text );
 298+ }
 299+
 300+ if ( $aprops !== '' ) $aprops = ' ' . $aprops;
 301+
 302+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 303+ $r = "<a href=\"{$u}\"{$style}{$aprops}>{$prefix}{$text}{$inside}</a>{$trail}";
 304+ wfProfileOut( $fname );
 305+ return $r;
 306+ }
 307+
 308+ /**
 309+ * Make a red link to the edit page of a given title.
 310+ *
 311+ * @param $title String: The text of the title
 312+ * @param $text String: Link text
 313+ * @param $query String: Optional query part
 314+ * @param $trail String: Optional trail. Alphabetic characters at the start of this string will
 315+ * be included in the link text. Other characters will be appended after
 316+ * the end of the link.
 317+ */
 318+ function makeBrokenLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 319+ # Fail gracefully
 320+ if ( ! isset($nt) ) {
 321+ # throw new MWException();
 322+ return "<!-- ERROR -->{$prefix}{$text}{$trail}";
 323+ }
 324+
 325+ $fname = 'Linker::makeBrokenLinkObj';
 326+ wfProfileIn( $fname );
 327+
 328+ if ( '' == $query ) {
 329+ $q = 'action=edit';
 330+ } else {
 331+ $q = 'action=edit&'.$query;
 332+ }
 333+ $u = $nt->escapeLocalURL( $q );
 334+
 335+ if ( '' == $text ) {
 336+ $text = htmlspecialchars( $nt->getPrefixedText() );
 337+ }
 338+ $style = $this->getInternalLinkAttributesObj( $nt, $text, "yes" );
 339+
 340+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 341+ $s = "<a href=\"{$u}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
 342+
 343+ wfProfileOut( $fname );
 344+ return $s;
 345+ }
 346+
 347+ /**
 348+ * Make a brown link to a short article.
 349+ *
 350+ * @param $title String: the text of the title
 351+ * @param $text String: link text
 352+ * @param $query String: optional query part
 353+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 354+ * be included in the link text. Other characters will be appended after
 355+ * the end of the link.
 356+ */
 357+ function makeStubLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 358+ $style = $this->getInternalLinkAttributesObj( $nt, $text, 'stub' );
 359+ return $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix, '', $style );
 360+ }
 361+
 362+ /**
 363+ * Generate either a normal exists-style link or a stub link, depending
 364+ * on the given page size.
 365+ *
 366+ * @param $size Integer
 367+ * @param $nt Title object.
 368+ * @param $text String
 369+ * @param $query String
 370+ * @param $trail String
 371+ * @param $prefix String
 372+ * @return string HTML of link
 373+ */
 374+ function makeSizeLinkObj( $size, $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 375+ global $wgUser;
 376+ $threshold = intval( $wgUser->getOption( 'stubthreshold' ) );
 377+ if( $size < $threshold ) {
 378+ return $this->makeStubLinkObj( $nt, $text, $query, $trail, $prefix );
 379+ } else {
 380+ return $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
 381+ }
 382+ }
 383+
 384+ /**
 385+ * Make appropriate markup for a link to the current article. This is currently rendered
 386+ * as the bold link text. The calling sequence is the same as the other make*LinkObj functions,
 387+ * despite $query not being used.
 388+ */
 389+ function makeSelfLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 390+ if ( '' == $text ) {
 391+ $text = htmlspecialchars( $nt->getPrefixedText() );
 392+ }
 393+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 394+ return "<strong class=\"selflink\">{$prefix}{$text}{$inside}</strong>{$trail}";
 395+ }
 396+
 397+ /** @todo document */
 398+ function fnamePart( $url ) {
 399+ $basename = strrchr( $url, '/' );
 400+ if ( false === $basename ) {
 401+ $basename = $url;
 402+ } else {
 403+ $basename = substr( $basename, 1 );
 404+ }
 405+ return htmlspecialchars( $basename );
 406+ }
 407+
 408+ /** Obsolete alias */
 409+ function makeImage( $url, $alt = '' ) {
 410+ return $this->makeExternalImage( $url, $alt );
 411+ }
 412+
 413+ /** @todo document */
 414+ function makeExternalImage( $url, $alt = '' ) {
 415+ if ( '' == $alt ) {
 416+ $alt = $this->fnamePart( $url );
 417+ }
 418+ $s = '<img src="'.$url.'" alt="'.$alt.'" />';
 419+ return $s;
 420+ }
 421+
 422+ /** @todo document */
 423+ function makeImageLinkObj( $nt, $label, $alt, $align = '', $params = array(), $framed = false,
 424+ $thumb = false, $manual_thumb = '', $valign = '' )
 425+ {
 426+ global $wgContLang, $wgUser, $wgThumbLimits;
 427+
 428+ $img = new Image( $nt );
 429+
 430+ if ( !$img->allowInlineDisplay() && $img->exists() ) {
 431+ return $this->makeKnownLinkObj( $nt );
 432+ }
 433+
 434+ $error = $prefix = $postfix = '';
 435+ $page = isset( $params['page'] ) ? $params['page'] : false;
 436+
 437+ if ( 'center' == $align )
 438+ {
 439+ $prefix = '<div class="center">';
 440+ $postfix = '</div>';
 441+ $align = 'none';
 442+ }
 443+
 444+ if ( !isset( $params['width'] ) ) {
 445+ $params['width'] = $img->getWidth( $page );
 446+ if( $thumb || $framed ) {
 447+ $wopt = $wgUser->getOption( 'thumbsize' );
 448+
 449+ if( !isset( $wgThumbLimits[$wopt] ) ) {
 450+ $wopt = User::getDefaultOption( 'thumbsize' );
 451+ }
 452+
 453+ $params['width'] = min( $params['width'], $wgThumbLimits[$wopt] );
 454+ }
 455+ }
 456+
 457+ if ( $thumb || $framed ) {
 458+
 459+ # Create a thumbnail. Alignment depends on language
 460+ # writing direction, # right aligned for left-to-right-
 461+ # languages ("Western languages"), left-aligned
 462+ # for right-to-left-languages ("Semitic languages")
 463+ #
 464+ # If thumbnail width has not been provided, it is set
 465+ # to the default user option as specified in Language*.php
 466+ if ( $align == '' ) {
 467+ $align = $wgContLang->isRTL() ? 'left' : 'right';
 468+ }
 469+ return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $params, $framed, $manual_thumb ).$postfix;
 470+ }
 471+
 472+ if ( $params['width'] && $img->exists() ) {
 473+ # Create a resized image, without the additional thumbnail features
 474+ $thumb = $img->transform( $params );
 475+ } else {
 476+ $thumb = false;
 477+ }
 478+
 479+ if ( $page ) {
 480+ $query = 'page=' . urlencode( $page );
 481+ } else {
 482+ $query = '';
 483+ }
 484+ $u = $nt->getLocalURL( $query );
 485+ $imgAttribs = array(
 486+ 'alt' => $alt,
 487+ 'longdesc' => $u
 488+ );
 489+ if ( $valign ) {
 490+ $imgAttribs['style'] = "vertical-align: $valign";
 491+ }
 492+ $linkAttribs = array(
 493+ 'href' => $u,
 494+ 'class' => 'image',
 495+ 'title' => $alt
 496+ );
 497+
 498+ if ( !$thumb ) {
 499+ $s = $this->makeBrokenImageLinkObj( $img->getTitle() );
 500+ } else {
 501+ $s = $thumb->toHtml( $imgAttribs, $linkAttribs );
 502+ }
 503+ if ( '' != $align ) {
 504+ $s = "<div class=\"float{$align}\"><span>{$s}</span></div>";
 505+ }
 506+ return str_replace("\n", ' ',$prefix.$s.$postfix);
 507+ }
 508+
 509+ /**
 510+ * Make HTML for a thumbnail including image, border and caption
 511+ * $img is an Image object
 512+ */
 513+ function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manual_thumb = "" ) {
 514+ global $wgStylePath, $wgContLang;
 515+ $thumbUrl = '';
 516+ $error = '';
 517+
 518+ $page = isset( $params['page'] ) ? $params['page'] : false;
 519+
 520+ if ( empty( $params['width'] ) ) {
 521+ $params['width'] = 180;
 522+ }
 523+ $thumb = false;
 524+ if ( $manual_thumb != '' ) {
 525+ # Use manually specified thumbnail
 526+ $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb );
 527+ if( $manual_title ) {
 528+ $manual_img = new Image( $manual_title );
 529+ $thumb = $manual_img->getUnscaledThumb();
 530+ }
 531+ } elseif ( $framed ) {
 532+ // Use image dimensions, don't scale
 533+ $thumb = $img->getUnscaledThumb( $page );
 534+ } else {
 535+ $thumb = $img->transform( $params );
 536+ }
 537+
 538+ if ( $thumb ) {
 539+ $outerWidth = $thumb->getWidth() + 2;
 540+ } else {
 541+ $outerWidth = $params['width'] + 2;
 542+ }
 543+
 544+ $query = $page ? 'page=' . urlencode( $page ) : '';
 545+ $u = $img->getTitle()->getLocalURL( $query );
 546+
 547+ $more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
 548+ $magnifyalign = $wgContLang->isRTL() ? 'left' : 'right';
 549+ $textalign = $wgContLang->isRTL() ? ' style="text-align:right"' : '';
 550+
 551+ $s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
 552+ if ( !$thumb ) {
 553+ $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
 554+ $zoomicon = '';
 555+ } elseif( !$img->exists() ) {
 556+ $s .= $this->makeBrokenImageLinkObj( $img->getTitle() );
 557+ $zoomicon = '';
 558+ } else {
 559+ $imgAttribs = array(
 560+ 'alt' => $alt,
 561+ 'longdesc' => $u,
 562+ 'class' => 'thumbimage'
 563+ );
 564+ $linkAttribs = array(
 565+ 'href' => $u,
 566+ 'class' => 'internal',
 567+ 'title' => $alt
 568+ );
 569+
 570+ $s .= $thumb->toHtml( $imgAttribs, $linkAttribs );
 571+ if ( $framed ) {
 572+ $zoomicon="";
 573+ } else {
 574+ $zoomicon = '<div class="magnify" style="float:'.$magnifyalign.'">'.
 575+ '<a href="'.$u.'" class="internal" title="'.$more.'">'.
 576+ '<img src="'.$wgStylePath.'/common/images/magnify-clip.png" ' .
 577+ 'width="15" height="11" alt="" /></a></div>';
 578+ }
 579+ }
 580+ $s .= ' <div class="thumbcaption"'.$textalign.'>'.$zoomicon.$label."</div></div></div>";
 581+ return str_replace("\n", ' ', $s);
 582+ }
 583+
 584+ /**
 585+ * Pass a title object, not a title string
 586+ */
 587+ function makeBrokenImageLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 588+ # Fail gracefully
 589+ if ( ! isset($nt) ) {
 590+ # throw new MWException();
 591+ return "<!-- ERROR -->{$prefix}{$text}{$trail}";
 592+ }
 593+
 594+ $fname = 'Linker::makeBrokenImageLinkObj';
 595+ wfProfileIn( $fname );
 596+
 597+ $q = 'wpDestFile=' . urlencode( $nt->getDBkey() );
 598+ if ( '' != $query ) {
 599+ $q .= "&$query";
 600+ }
 601+ $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
 602+ $url = $uploadTitle->escapeLocalURL( $q );
 603+
 604+ if ( '' == $text ) {
 605+ $text = htmlspecialchars( $nt->getPrefixedText() );
 606+ }
 607+ $style = $this->getInternalLinkAttributesObj( $nt, $text, "yes" );
 608+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 609+ $s = "<a href=\"{$url}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
 610+
 611+ wfProfileOut( $fname );
 612+ return $s;
 613+ }
 614+
 615+ /** @todo document */
 616+ function makeMediaLink( $name, /* wtf?! */ $url, $alt = '' ) {
 617+ $nt = Title::makeTitleSafe( NS_IMAGE, $name );
 618+ return $this->makeMediaLinkObj( $nt, $alt );
 619+ }
 620+
 621+ /**
 622+ * Create a direct link to a given uploaded file.
 623+ *
 624+ * @param $title Title object.
 625+ * @param $text String: pre-sanitized HTML
 626+ * @return string HTML
 627+ *
 628+ * @public
 629+ * @todo Handle invalid or missing images better.
 630+ */
 631+ function makeMediaLinkObj( $title, $text = '' ) {
 632+ if( is_null( $title ) ) {
 633+ ### HOTFIX. Instead of breaking, return empty string.
 634+ return $text;
 635+ } else {
 636+ $img = new Image( $title );
 637+ if( $img->exists() ) {
 638+ $url = $img->getURL();
 639+ $class = 'internal';
 640+ } else {
 641+ $upload = SpecialPage::getTitleFor( 'Upload' );
 642+ $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $img->getName() ) );
 643+ $class = 'new';
 644+ }
 645+ $alt = htmlspecialchars( $title->getText() );
 646+ if( $text == '' ) {
 647+ $text = $alt;
 648+ }
 649+ $u = htmlspecialchars( $url );
 650+ return "<a href=\"{$u}\" class=\"$class\" title=\"{$alt}\">{$text}</a>";
 651+ }
 652+ }
 653+
 654+ /** @todo document */
 655+ function specialLink( $name, $key = '' ) {
 656+ global $wgContLang;
 657+
 658+ if ( '' == $key ) { $key = strtolower( $name ); }
 659+ $pn = $wgContLang->ucfirst( $name );
 660+ return $this->makeKnownLink( $wgContLang->specialPage( $pn ),
 661+ wfMsg( $key ) );
 662+ }
 663+
 664+ /** @todo document */
 665+ function makeExternalLink( $url, $text, $escape = true, $linktype = '', $ns = null ) {
 666+ $style = $this->getExternalLinkAttributes( $url, $text, 'external ' . $linktype );
 667+ global $wgNoFollowLinks, $wgNoFollowNsExceptions;
 668+ if( $wgNoFollowLinks && !(isset($ns) && in_array($ns, $wgNoFollowNsExceptions)) ) {
 669+ $style .= ' rel="nofollow"';
 670+ }
 671+ $url = htmlspecialchars( $url );
 672+ if( $escape ) {
 673+ $text = htmlspecialchars( $text );
 674+ }
 675+ return '<a href="'.$url.'"'.$style.'>'.$text.'</a>';
 676+ }
 677+
 678+ /**
 679+ * Make user link (or user contributions for unregistered users)
 680+ * @param $userId Integer: user id in database.
 681+ * @param $userText String: user name in database
 682+ * @return string HTML fragment
 683+ * @private
 684+ */
 685+ function userLink( $userId, $userText ) {
 686+ $encName = htmlspecialchars( $userText );
 687+ if( $userId == 0 ) {
 688+ $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
 689+ return $this->makeKnownLinkObj( $contribsPage,
 690+ $encName);
 691+ } else {
 692+ $userPage = Title::makeTitle( NS_USER, $userText );
 693+ return $this->makeLinkObj( $userPage, $encName );
 694+ }
 695+ }
 696+
 697+ /**
 698+ * @param $userId Integer: user id in database.
 699+ * @param $userText String: user name in database.
 700+ * @param $redContribsWhenNoEdits Bool: return a red contribs link when the user had no edits and this is true.
 701+ * @return string HTML fragment with talk and/or block links
 702+ */
 703+ public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false ) {
 704+ global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
 705+ $talkable = !( $wgDisableAnonTalk && 0 == $userId );
 706+ $blockable = ( $wgSysopUserBans || 0 == $userId );
 707+
 708+ $items = array();
 709+ if( $talkable ) {
 710+ $items[] = $this->userTalkLink( $userId, $userText );
 711+ }
 712+ if( $userId ) {
 713+ // check if the user has an edit
 714+ if( $redContribsWhenNoEdits && User::edits( $userId ) == 0 ) {
 715+ $style = "class='new'";
 716+ } else {
 717+ $style = '';
 718+ }
 719+ $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
 720+
 721+ $items[] = $this->makeKnownLinkObj( $contribsPage, wfMsgHtml( 'contribslink' ), '', '', '', '', $style );
 722+ }
 723+ if( $blockable && $wgUser->isAllowed( 'block' ) ) {
 724+ $items[] = $this->blockLink( $userId, $userText );
 725+ }
 726+
 727+ if( $items ) {
 728+ return ' (' . implode( ' | ', $items ) . ')';
 729+ } else {
 730+ return '';
 731+ }
 732+ }
 733+
 734+ /**
 735+ * Alias for userToolLinks( $userId, $userText, true );
 736+ */
 737+ public function userToolLinksRedContribs( $userId, $userText ) {
 738+ return $this->userToolLinks( $userId, $userText, true );
 739+ }
 740+
 741+
 742+ /**
 743+ * @param $userId Integer: user id in database.
 744+ * @param $userText String: user name in database.
 745+ * @return string HTML fragment with user talk link
 746+ * @private
 747+ */
 748+ function userTalkLink( $userId, $userText ) {
 749+ $userTalkPage = Title::makeTitle( NS_USER_TALK, $userText );
 750+ $userTalkLink = $this->makeLinkObj( $userTalkPage, wfMsgHtml( 'talkpagelinktext' ) );
 751+ return $userTalkLink;
 752+ }
 753+
 754+ /**
 755+ * @param $userId Integer: userid
 756+ * @param $userText String: user name in database.
 757+ * @return string HTML fragment with block link
 758+ * @private
 759+ */
 760+ function blockLink( $userId, $userText ) {
 761+ $blockPage = SpecialPage::getTitleFor( 'Blockip', $userText );
 762+ $blockLink = $this->makeKnownLinkObj( $blockPage,
 763+ wfMsgHtml( 'blocklink' ) );
 764+ return $blockLink;
 765+ }
 766+
 767+ /**
 768+ * Generate a user link if the current user is allowed to view it
 769+ * @param $rev Revision object.
 770+ * @return string HTML
 771+ */
 772+ function revUserLink( $rev ) {
 773+ if( $rev->userCan( Revision::DELETED_USER ) ) {
 774+ $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() );
 775+ } else {
 776+ $link = wfMsgHtml( 'rev-deleted-user' );
 777+ }
 778+ if( $rev->isDeleted( Revision::DELETED_USER ) ) {
 779+ return '<span class="history-deleted">' . $link . '</span>';
 780+ }
 781+ return $link;
 782+ }
 783+
 784+ /**
 785+ * Generate a user tool link cluster if the current user is allowed to view it
 786+ * @param $rev Revision object.
 787+ * @return string HTML
 788+ */
 789+ function revUserTools( $rev ) {
 790+ if( $rev->userCan( Revision::DELETED_USER ) ) {
 791+ $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ) .
 792+ ' ' .
 793+ $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() );
 794+ } else {
 795+ $link = wfMsgHtml( 'rev-deleted-user' );
 796+ }
 797+ if( $rev->isDeleted( Revision::DELETED_USER ) ) {
 798+ return '<span class="history-deleted">' . $link . '</span>';
 799+ }
 800+ return $link;
 801+ }
 802+
 803+ /**
 804+ * This function is called by all recent changes variants, by the page history,
 805+ * and by the user contributions list. It is responsible for formatting edit
 806+ * comments. It escapes any HTML in the comment, but adds some CSS to format
 807+ * auto-generated comments (from section editing) and formats [[wikilinks]].
 808+ *
 809+ * @author Erik Moeller <moeller@scireview.de>
 810+ *
 811+ * Note: there's not always a title to pass to this function.
 812+ * Since you can't set a default parameter for a reference, I've turned it
 813+ * temporarily to a value pass. Should be adjusted further. --brion
 814+ *
 815+ * @param string $comment
 816+ * @param mixed $title Title object (to generate link to the section in autocomment) or null
 817+ * @param bool $local Whether section links should refer to local page
 818+ */
 819+ function formatComment($comment, $title = NULL, $local = false) {
 820+ wfProfileIn( __METHOD__ );
 821+
 822+ global $wgContLang;
 823+ $comment = str_replace( "\n", " ", $comment );
 824+ $comment = htmlspecialchars( $comment );
 825+
 826+ # The pattern for autogen comments is / * foo * /, which makes for
 827+ # some nasty regex.
 828+ # We look for all comments, match any text before and after the comment,
 829+ # add a separator where needed and format the comment itself with CSS
 830+ $match = array();
 831+ while (preg_match('/(.*)\/\*\s*(.*?)\s*\*\/(.*)/', $comment,$match)) {
 832+ $pre=$match[1];
 833+ $auto=$match[2];
 834+ $post=$match[3];
 835+ $link='';
 836+ if( $title ) {
 837+ $section = $auto;
 838+
 839+ # Generate a valid anchor name from the section title.
 840+ # Hackish, but should generally work - we strip wiki
 841+ # syntax, including the magic [[: that is used to
 842+ # "link rather than show" in case of images and
 843+ # interlanguage links.
 844+ $section = str_replace( '[[:', '', $section );
 845+ $section = str_replace( '[[', '', $section );
 846+ $section = str_replace( ']]', '', $section );
 847+ if ( $local ) {
 848+ $sectionTitle = Title::newFromText( '#' . $section);
 849+ } else {
 850+ $sectionTitle = wfClone( $title );
 851+ $sectionTitle->mFragment = $section;
 852+ }
 853+ $link = $this->makeKnownLinkObj( $sectionTitle, wfMsg( 'sectionlink' ) );
 854+ }
 855+ $sep='-';
 856+ $auto=$link.$auto;
 857+ if($pre) { $auto = $sep.' '.$auto; }
 858+ if($post) { $auto .= ' '.$sep; }
 859+ $auto='<span class="autocomment">'.$auto.'</span>';
 860+ $comment=$pre.$auto.$post;
 861+ }
 862+
 863+ # format regular and media links - all other wiki formatting
 864+ # is ignored
 865+ $medians = '(?:' . preg_quote( Namespace::getCanonicalName( NS_MEDIA ), '/' ) . '|';
 866+ $medians .= preg_quote( $wgContLang->getNsText( NS_MEDIA ), '/' ) . '):';
 867+ while(preg_match('/\[\[:?(.*?)(\|(.*?))*\]\](.*)$/',$comment,$match)) {
 868+ # Handle link renaming [[foo|text]] will show link as "text"
 869+ if( "" != $match[3] ) {
 870+ $text = $match[3];
 871+ } else {
 872+ $text = $match[1];
 873+ }
 874+ $submatch = array();
 875+ if( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) {
 876+ # Media link; trail not supported.
 877+ $linkRegexp = '/\[\[(.*?)\]\]/';
 878+ $thelink = $this->makeMediaLink( $submatch[1], "", $text );
 879+ } else {
 880+ # Other kind of link
 881+ if( preg_match( $wgContLang->linkTrail(), $match[4], $submatch ) ) {
 882+ $trail = $submatch[1];
 883+ } else {
 884+ $trail = "";
 885+ }
 886+ $linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/';
 887+ if (isset($match[1][0]) && $match[1][0] == ':')
 888+ $match[1] = substr($match[1], 1);
 889+ $thelink = $this->makeLink( $match[1], $text, "", $trail );
 890+ }
 891+ $comment = preg_replace( $linkRegexp, StringUtils::escapeRegexReplacement( $thelink ), $comment, 1 );
 892+ }
 893+ wfProfileOut( __METHOD__ );
 894+ return $comment;
 895+ }
 896+
 897+ /**
 898+ * Wrap a comment in standard punctuation and formatting if
 899+ * it's non-empty, otherwise return empty string.
 900+ *
 901+ * @param string $comment
 902+ * @param mixed $title Title object (to generate link to section in autocomment) or null
 903+ * @param bool $local Whether section links should refer to local page
 904+ *
 905+ * @return string
 906+ */
 907+ function commentBlock( $comment, $title = NULL, $local = false ) {
 908+ // '*' used to be the comment inserted by the software way back
 909+ // in antiquity in case none was provided, here for backwards
 910+ // compatability, acc. to brion -ævar
 911+ if( $comment == '' || $comment == '*' ) {
 912+ return '';
 913+ } else {
 914+ $formatted = $this->formatComment( $comment, $title, $local );
 915+ return " <span class=\"comment\">($formatted)</span>";
 916+ }
 917+ }
 918+
 919+ /**
 920+ * Wrap and format the given revision's comment block, if the current
 921+ * user is allowed to view it.
 922+ *
 923+ * @param Revision $rev
 924+ * @param bool $local Whether section links should refer to local page
 925+ * @return string HTML
 926+ */
 927+ function revComment( Revision $rev, $local = false ) {
 928+ if( $rev->userCan( Revision::DELETED_COMMENT ) ) {
 929+ $block = $this->commentBlock( $rev->getRawComment(), $rev->getTitle(), $local );
 930+ } else {
 931+ $block = " <span class=\"comment\">" .
 932+ wfMsgHtml( 'rev-deleted-comment' ) . "</span>";
 933+ }
 934+ if( $rev->isDeleted( Revision::DELETED_COMMENT ) ) {
 935+ return " <span class=\"history-deleted\">$block</span>";
 936+ }
 937+ return $block;
 938+ }
 939+
 940+ /** @todo document */
 941+ function tocIndent() {
 942+ return "\n<ul>";
 943+ }
 944+
 945+ /** @todo document */
 946+ function tocUnindent($level) {
 947+ return "</li>\n" . str_repeat( "</ul>\n</li>\n", $level>0 ? $level : 0 );
 948+ }
 949+
 950+ /**
 951+ * parameter level defines if we are on an indentation level
 952+ */
 953+ function tocLine( $anchor, $tocline, $tocnumber, $level ) {
 954+ return "\n<li class=\"toclevel-$level\"><a href=\"#" .
 955+ $anchor . '"><span class="tocnumber">' .
 956+ $tocnumber . '</span> <span class="toctext">' .
 957+ $tocline . '</span></a>';
 958+ }
 959+
 960+ /** @todo document */
 961+ function tocLineEnd() {
 962+ return "</li>\n";
 963+ }
 964+
 965+ /** @todo document */
 966+ function tocList($toc) {
 967+ global $wgJsMimeType;
 968+ $title = wfMsgHtml('toc') ;
 969+ return
 970+ '<table id="toc" class="toc" summary="' . $title .'"><tr><td>'
 971+ . '<div id="toctitle"><h2>' . $title . "</h2></div>\n"
 972+ . $toc
 973+ # no trailing newline, script should not be wrapped in a
 974+ # paragraph
 975+ . "</ul>\n</td></tr></table>"
 976+ . '<script type="' . $wgJsMimeType . '">'
 977+ . ' if (window.showTocToggle) {'
 978+ . ' var tocShowText = "' . wfEscapeJsString( wfMsg('showtoc') ) . '";'
 979+ . ' var tocHideText = "' . wfEscapeJsString( wfMsg('hidetoc') ) . '";'
 980+ . ' showTocToggle();'
 981+ . ' } '
 982+ . "</script>\n";
 983+ }
 984+
 985+ /** @todo document */
 986+ public function editSectionLinkForOther( $title, $section ) {
 987+ global $wgContLang;
 988+
 989+ $title = Title::newFromText( $title );
 990+ $editurl = '&section='.$section;
 991+ $url = $this->makeKnownLinkObj( $title, wfMsg('editsection'), 'action=edit'.$editurl );
 992+
 993+### - return "<span class=\"editsection\">[".$url."]</span>";
 994+ /** Added editSectionLinkForOther hook that allows section/header link to be modified */
 995+ $result = null;
 996+ wfRunHooks( 'EditSectionLinkForOther', array( &$this, $title, $section, $url, &$result ) );
 997+ return is_null( $result )
 998+ ? "<span class=\"editsection\">[{$url}]</span>"
 999+ : "<span class=\"editsection\">[{$result}]</span>";
 1000+ /** End of hook */
 1001+ }
 1002+
 1003+ /**
 1004+ * @param $title Title object.
 1005+ * @param $section Integer: section number.
 1006+ * @param $hint Link String: title, or default if omitted or empty
 1007+ */
 1008+ public function editSectionLink( $nt, $section, $hint='' ) {
 1009+ global $wgContLang;
 1010+
 1011+ $editurl = '&section='.$section;
 1012+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) ) . '"';
 1013+ $url = $this->makeKnownLinkObj( $nt, wfMsg('editsection'), 'action=edit'.$editurl, '', '', '', $hint );
 1014+
 1015+### - return "<span class=\"editsection\">[".$url."]</span>";
 1016+ /** Added editSectionLink hook that allows section/header link to be modified */
 1017+ $result = null;
 1018+ wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $hint, $url, &$result ) );
 1019+ return is_null( $result )
 1020+ ? "<span class=\"editsection\">[{$url}]</span>"
 1021+ : "<span class=\"editsection\">[{$result}]</span>";
 1022+ /** End of hook */
 1023+ }
 1024+
 1025+ /**
 1026+ * Create a headline for content
 1027+ *
 1028+ * @param int $level The level of the headline (1-6)
 1029+ * @param string $attribs Any attributes for the headline, starting with a space and ending with '>'
 1030+ * This *must* be at least '>' for no attribs
 1031+ * @param string $anchor The anchor to give the headline (the bit after the #)
 1032+ * @param string $text The text of the header
 1033+ * @param string $link HTML to add for the section edit link
 1034+ *
 1035+ * @return string HTML headline
 1036+ */
 1037+ public function makeHeadline( $level, $attribs, $anchor, $text, $link ) {
 1038+ return "<a name=\"$anchor\"></a><h$level$attribs$link <span class=\"mw-headline\">$text</span></h$level>";
 1039+ }
 1040+
 1041+ /**
 1042+ * Split a link trail, return the "inside" portion and the remainder of the trail
 1043+ * as a two-element array
 1044+ *
 1045+ * @static
 1046+ */
 1047+ static function splitTrail( $trail ) {
 1048+ static $regex = false;
 1049+ if ( $regex === false ) {
 1050+ global $wgContLang;
 1051+ $regex = $wgContLang->linkTrail();
 1052+ }
 1053+ $inside = '';
 1054+ if ( '' != $trail ) {
 1055+ $m = array();
 1056+ if ( preg_match( $regex, $trail, $m ) ) {
 1057+ $inside = $m[1];
 1058+ $trail = $m[2];
 1059+ }
 1060+ }
 1061+ return array( $inside, $trail );
 1062+ }
 1063+
 1064+ /**
 1065+ * Generate a rollback link for a given revision. Currently it's the
 1066+ * caller's responsibility to ensure that the revision is the top one. If
 1067+ * it's not, of course, the user will get an error message.
 1068+ *
 1069+ * If the calling page is called with the parameter &bot=1, all rollback
 1070+ * links also get that parameter. It causes the edit itself and the rollback
 1071+ * to be marked as "bot" edits. Bot edits are hidden by default from recent
 1072+ * changes, so this allows sysops to combat a busy vandal without bothering
 1073+ * other users.
 1074+ *
 1075+ * @param Revision $rev
 1076+ */
 1077+ function generateRollback( $rev ) {
 1078+ global $wgUser, $wgRequest;
 1079+ $title = $rev->getTitle();
 1080+
 1081+ $extraRollback = $wgRequest->getBool( 'bot' ) ? '&bot=1' : '';
 1082+ $extraRollback .= '&token=' . urlencode(
 1083+ $wgUser->editToken( array( $title->getPrefixedText(), $rev->getUserText() ) ) );
 1084+ return '<span class="mw-rollback-link">['. $this->makeKnownLinkObj( $title,
 1085+ wfMsg('rollbacklink'),
 1086+ 'action=rollback&from=' . urlencode( $rev->getUserText() ) . $extraRollback ) .']</span>';
 1087+ }
 1088+
 1089+ /**
 1090+ * Returns HTML for the "templates used on this page" list.
 1091+ *
 1092+ * @param array $templates Array of templates from Article::getUsedTemplate
 1093+ * or similar
 1094+ * @param bool $preview Whether this is for a preview
 1095+ * @param bool $section Whether this is for a section edit
 1096+ * @return string HTML output
 1097+ */
 1098+ public function formatTemplates( $templates, $preview = false, $section = false) {
 1099+ global $wgUser;
 1100+ wfProfileIn( __METHOD__ );
 1101+
 1102+ $sk = $wgUser->getSkin();
 1103+
 1104+ $outText = '';
 1105+ if ( count( $templates ) > 0 ) {
 1106+ # Do a batch existence check
 1107+ $batch = new LinkBatch;
 1108+ foreach( $templates as $title ) {
 1109+ $batch->addObj( $title );
 1110+ }
 1111+ $batch->execute();
 1112+
 1113+ # Construct the HTML
 1114+ $outText = '<div class="mw-templatesUsedExplanation">';
 1115+ if ( $preview ) {
 1116+ $outText .= wfMsgExt( 'templatesusedpreview', array( 'parse' ) );
 1117+ } elseif ( $section ) {
 1118+ $outText .= wfMsgExt( 'templatesusedsection', array( 'parse' ) );
 1119+ } else {
 1120+ $outText .= wfMsgExt( 'templatesused', array( 'parse' ) );
 1121+ }
 1122+ $outText .= '</div><ul>';
 1123+
 1124+ foreach ( $templates as $titleObj ) {
 1125+ $r = $titleObj->getRestrictions( 'edit' );
 1126+ if ( in_array( 'sysop', $r ) ) {
 1127+ $protected = wfMsgExt( 'template-protected', array( 'parseinline' ) );
 1128+ } elseif ( in_array( 'autoconfirmed', $r ) ) {
 1129+ $protected = wfMsgExt( 'template-semiprotected', array( 'parseinline' ) );
 1130+ } else {
 1131+ $protected = '';
 1132+ }
 1133+ $outText .= '<li>' . $sk->makeLinkObj( $titleObj ) . ' ' . $protected . '</li>';
 1134+ }
 1135+ $outText .= '</ul>';
 1136+ }
 1137+ wfProfileOut( __METHOD__ );
 1138+ return $outText;
 1139+ }
 1140+
 1141+ /**
 1142+ * Format a size in bytes for output, using an appropriate
 1143+ * unit (B, KB, MB or GB) according to the magnitude in question
 1144+ *
 1145+ * @param $size Size to format
 1146+ * @return string
 1147+ */
 1148+ public function formatSize( $size ) {
 1149+ global $wgLang;
 1150+ // For small sizes no decimal places necessary
 1151+ $round = 0;
 1152+ if( $size > 1024 ) {
 1153+ $size = $size / 1024;
 1154+ if( $size > 1024 ) {
 1155+ $size = $size / 1024;
 1156+ // For MB and bigger two decimal places are smarter
 1157+ $round = 2;
 1158+ if( $size > 1024 ) {
 1159+ $size = $size / 1024;
 1160+ $msg = 'size-gigabytes';
 1161+ } else {
 1162+ $msg = 'size-megabytes';
 1163+ }
 1164+ } else {
 1165+ $msg = 'size-kilobytes';
 1166+ }
 1167+ } else {
 1168+ $msg = 'size-bytes';
 1169+ }
 1170+ $size = round( $size, $round );
 1171+ return wfMsgHtml( $msg, $wgLang->formatNum( $size ) );
 1172+ }
 1173+
 1174+ /**
 1175+ * Given the id of an interface element, constructs the appropriate title
 1176+ * and accesskey attributes from the system messages. (Note, this is usu-
 1177+ * ally the id but isn't always, because sometimes the accesskey needs to
 1178+ * go on a different element than the id, for reverse-compatibility, etc.)
 1179+ *
 1180+ * @param string $name Id of the element, minus prefixes.
 1181+ * @return string title and accesskey attributes, ready to drop in an
 1182+ * element (e.g., ' title="This does something [x]" accesskey="x"').
 1183+ */
 1184+ public function tooltipAndAccesskey($name) {
 1185+ $out = '';
 1186+
 1187+ $tooltip = wfMsg('tooltip-'.$name);
 1188+ if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') {
 1189+ // Compatibility: formerly some tooltips had [alt-.] hardcoded
 1190+ $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
 1191+ $out .= ' title="'.htmlspecialchars($tooltip);
 1192+ }
 1193+ $accesskey = wfMsg('accesskey-'.$name);
 1194+ if ($accesskey && $accesskey != '-' && !wfEmptyMsg('accesskey-'.$name, $accesskey)) {
 1195+ if ($out) $out .= " [$accesskey]\" accesskey=\"$accesskey\"";
 1196+ else $out .= " title=\"[$accesskey]\" accesskey=\"$accesskey\"";
 1197+ } elseif ($out) {
 1198+ $out .= '"';
 1199+ }
 1200+ return $out;
 1201+ }
 1202+
 1203+ /**
 1204+ * Given the id of an interface element, constructs the appropriate title
 1205+ * attribute from the system messages. (Note, this is usually the id but
 1206+ * isn't always, because sometimes the accesskey needs to go on a different
 1207+ * element than the id, for reverse-compatibility, etc.)
 1208+ *
 1209+ * @param string $name Id of the element, minus prefixes.
 1210+ * @return string title attribute, ready to drop in an element
 1211+ * (e.g., ' title="This does something"').
 1212+ */
 1213+ public function tooltip($name) {
 1214+ $out = '';
 1215+
 1216+ $tooltip = wfMsg('tooltip-'.$name);
 1217+ if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') {
 1218+ $out = ' title="'.htmlspecialchars($tooltip).'"';
 1219+ }
 1220+
 1221+ return $out;
 1222+ }
 1223+}
 1224+
 1225+?>
Index: trunk/extensions/DiscussionThreading/DiscussionThreading.i18n.php
@@ -0,0 +1,39 @@
 2+<?php
 3+/**
 4+ * Internationalisation file for DiscussionThreading extension.
 5+ *
 6+ * @addtogroup Extensions
 7+*/
 8+
 9+$wgDiscussionThreadMessages = array();
 10+
 11+$wgDiscussionThreadMessages['en'] = array(
 12+ 'replysection' => 'reply',
 13+ 'replysectionhint' => "Reply to this Posting",
 14+ 'threadnewsection' => 'new',
 15+ 'threadnewsectionhint' => "Start a new thread"
 16+);
 17+$wgDiscussionThreadMessages['es'] = array(
 18+ 'replysection' => 'respuesta',
 19+ 'replysectionhint' => "Respuesta a este tema",
 20+ 'threadnewsection' => 'nuevo',
 21+ 'threadnewsectionhint' => "Empezar un nuevo tema"
 22+);
 23+$wgDiscussionThreadMessages['de'] = array(
 24+ 'replysection' => 'antworten',
 25+ 'replysectionhint' => "Auf diesen Eintrag antworten",
 26+ 'threadnewsection' => 'neu',
 27+ 'threadnewsectionhint' => "Neuen Eintrag erstellen"
 28+);
 29+/* Need to add
 30+ af,br,bs,ca,cs,cy,et,eu,fi,fr,ga,gl,he,hr,hsb,id,is,it,ja,kk-kz,kk-tr,kk-cn,kk,lv,nl,
 31+ no,nn,oc,pt,pt-br,ro,ru,sk,sl,sq,uk,wa,zh-cn,zh-tw,zh-yue,zh-hk,zh-sg
 32+
 33+ Would do this by adding a new $wgDiscussionThreadMessages array example:
 34+
 35+$wgDiscussionThreadMessages['lang1'] = array(
 36+ 'replysection' => 'lang1tag',
 37+ 'replysectionhint' => "lang1hint",
 38+);
 39+*/
 40+?>
Index: trunk/extensions/DiscussionThreading/README
@@ -0,0 +1,368 @@
 2+'''Title: ''' Modify Discussion Functionality to be more threadlike
 3+
 4+'''Description:'''
 5+
 6+[[Extension:DiscussionThreading|See formal extension description]]
 7+
 8+The thread-like nature of discussions is highly valuable. This extension is a quasi threading adaption for the discussion feature that does the following:
 9+
 10+# Adds a <nowiki>[new]</nowiki> link onto the header that creates a new comment on the page when used.
 11+# Adds a <nowiki>[reply]</nowiki> link onto the header that creates a reply to that section.
 12+# Automatically adds a signature in at the top of the section - preferably in the header bar to show up in TOC
 13+# Enters the discussion page in the add comment mode (and automatic signature) when new discussion page made
 14+
 15+This requires adding 2 new hooks to Linker.php - which requires manual patching. The extension itself includes multiple hooks and the initial setup.
 16+
 17+== Installation Instructions ==
 18+
 19+Will absolutely not work below 1.6.0, requires features added with that release.
 20+
 21+The "reply" feature requires a new parameter passed to the EditPage. This parameter is "replyto=yes" when the user is replying to a previous post.
 22+
 23+You can apply this extension either by downloading and copying the files into your wiki, or by manual processes below. The referenced packages have the files necessary to implement without editing patches, but only for the following versions. :
 24+
 25+* REL1_10
 26+* REL1_9_3
 27+
 28+If you do not have one of these versions, you must [[Extension:DiscussionThread_Article#Manual_Patching_and_Installation | manually patch]].
 29+
 30+=== Download and Install Instructions ===
 31+
 32+You can download the extension and already patched files from:
 33+
 34+* [http://wiki.montcopa.org/PublicDownloads/DiscussionThreading.zip Windows Zip Package]
 35+* [http://wiki.montcopa.org/PublicDownloads/DiscussionThreading.tar TAR Package]
 36+
 37+# Create a new directory extensions/DiscussionThreading
 38+# Copy the files DiscussionThreading.php and DiscussionThreading.i18n.php into extensions/DiscussionThreading
 39+# Select the appropriate Release Directory ('''NOTE: DO NOT USE THESE UNLESS YOU HAVE THE EXACT RELEASE''') and perform ONLY ONE of the following two steps:
 40+## Apply the patch file to your existing includes/linker.php file
 41+## Copy the included linker.php file into your includes directory, overwriting the existing version (Note: If you have applied other patches to this file they will be overwritten).
 42+# Patch localsettings.php by adding the following near other require_once or at the end before the close statement:
 43+<pre>require_once("$IP/extensions/DiscussionThreading/DiscussionThreading.php");</pre>
 44+
 45+If you have executed this properly, you will have the following:
 46+
 47+* DiscussionThreading.php and DiscussionThreading.i18n.php moved into $IP/extensions/DiscussionThreading/DiscussionThreading.php
 48+* Either patched Linker.php or the loaded Linker.php from the EXACT distribution downloaded release in $IP/includes/
 49+* extensions/DiscussionThreading/DiscussionThreading.php executed from localsettings.php
 50+
 51+=== Manual Patching and Installation ===
 52+Details are below, but these are required steps:
 53+
 54+# Create a new directory extensions/DiscussionThreading
 55+# Create a new file extensions/DiscussionThreading/DiscussionThreading.php and copy and paste the below code
 56+# Create a new file extensions/DiscussionThreading/DiscussionThreading.i18n.php and copy and paste the below code
 57+# Patch Linker.php according to below instructions
 58+# Patch localsettings.php by adding the following near other require_once or at the end before the close statement:
 59+<pre>require_once("$IP/extensions/DiscussionThreading/DiscussionThreading.php");</pre>
 60+
 61+There is something very funky about flushing the PHP compiled cache. I was only able to get the changes made in the Linker.php file to take effect by going to a discussion page while I was logged on as an administrator on the patched server and using a browser local to that machine (press the + tab). Somehow, this caused the .php files to recompile and activate for everyone.
 62+
 63+== Patches Required ==
 64+
 65+To do this, have to patch the following (instructions are for 1.10.0, future or past releases will, of course vary with line numbers and locations):
 66+
 67+===Patches to Linker.php===
 68+
 69+====Modified linker.php editSectionLinkForOther (line 985 in REL1_10) ====
 70+<pre>
 71+ public function editSectionLinkForOther( $title, $section ) {
 72+ global $wgContLang;
 73+
 74+ $title = Title::newFromText( $title );
 75+ $editurl = '&section='.$section;
 76+ $url = $this->makeKnownLinkObj( $title, wfMsg('editsection'), 'action=edit'.$editurl );
 77+### Modifications Here
 78+### return "<span class=\"editsection\">[".$url."]</span>";
 79+ $result = null;
 80+ wfRunHooks( 'editSectionLinkForOther', array( &$this, $title, $section , $url, &$result) );
 81+ if ( $result == null ) {
 82+ return "<span class=\"editsection\">[".$url."]</span>";
 83+ } else {
 84+ return ($result);
 85+ }
 86+###
 87+ }
 88+</pre>
 89+====Modified linker.php editSectionLink (line 1004 in REL1_10) ====
 90+<pre>
 91+ public function editSectionLink( $nt, $section, $hint='' ) {
 92+ global $wgContLang;
 93+
 94+ $editurl = '&section='.$section;
 95+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) ) . '"';
 96+ $url = $this->makeKnownLinkObj( $nt, wfMsg('editsection'), 'action=edit'.$editurl, '', '', '', $hint );
 97+### Modifications Here
 98+### return "<span class=\"editsection\">[".$url."]</span>";
 99+ $result = null;
 100+ wfRunHooks( 'editSectionLink', array( &$this, $nt, $section, $hint='', $url , &$result) );
 101+ if ( $result == null ) {
 102+ return "<span class=\"editsection\">[".$url."]</span>";
 103+ } else {
 104+ return ($result);
 105+ }
 106+###
 107+ }
 108+</pre>
 109+
 110+== DiscussionThreading Extension ==
 111+
 112+extensions/DiscussionThreading.php
 113+
 114+This extension implements a discussion threading process. This threading will only be seen on the talk pages, unless explicity invoked in the command line.
 115+
 116+It adds a <nowiki>[reply]</nowiki> link on the header line. Each time a new posting is entered or a posting is replied to, the comment will automatically be tagged (and appear in the TOC too). Of course direct editing can occur, but this really makes it a lot easier to organize discussions in a threading view that most are familiar with.
 117+
 118+To do:
 119+# get someone who knows regex to clean up the parsing of the subject line
 120+# Assess to see if there's an easier way to do this
 121+
 122+<pre>
 123+<?php
 124+
 125+/**
 126+ * Extension to provide discussion threading similar to a listserv archive
 127+ *
 128+ * @author Jack D. Pond <jack.pond@psitex.com>
 129+ * @addtogroup Extensions
 130+ * @copyright � 2007 Jack D. pond
 131+ * @licence GNU General Public Licence 2.0 or later
 132+ */
 133+
 134+if( defined( 'MEDIAWIKI' ) ) {
 135+
 136+
 137+# Internationalisation file
 138+ require_once( "$IP/extensions/DiscussionThreading/DiscussionThreading.i18n.php" );
 139+
 140+ $wgExtensionFunctions[] = 'efDiscussionThreadSetup';
 141+ $wgExtensionCredits['other'][] = array(
 142+ 'name' => 'DiscussionThreading',
 143+ 'author' => 'Jack D. Pond',
 144+ 'url' => 'http://www.mediawiki.org/wiki/Extension:DiscussionThreading',
 145+ 'description' => 'Add Threading to discussion (talk) pages' );
 146+
 147+ /**
 148+ * Set up hooks for discussion threading
 149+ *
 150+ * @param $wgSectionThreadingOn global logical variable to activate threading
 151+ */
 152+
 153+
 154+ global $wgSectionThreadingOn;
 155+ $wgSectionThreadingOn = True;
 156+
 157+ $wgHooks['EditPage::showEditForm:initial'][] = 'efDiscussionThread';
 158+ $wgHooks['EditPage::attemptSave'][] = 'efStampReply';
 159+ $wgHooks['EditPage::showEditForm:initial'][] = 'efDiscussionThreadEdit';
 160+ $wgHooks['editSectionLinkForOther'][] = 'efDiscussionLink4other';
 161+ $wgHooks['editSectionLink'][] = 'efDiscussionLink';
 162+ $wgHooks['AlternateEdit'][] = 'efDiscussionThreadEdit';
 163+
 164+
 165+
 166+
 167+ /**
 168+ * Initial setup, add .i18n. messages from $IP/extensions/DiscussionThreading.i18n.php
 169+ */
 170+ function efDiscussionThreadSetup() {
 171+ global $wgMessageCache, $wgDiscussionThreadMessages;
 172+
 173+ foreach( $wgDiscussionThreadMessages as $lang => $messages )
 174+ $wgMessageCache->addMessages( $messages, $lang );
 175+ }
 176+ /**
 177+ * This function creates a linkobject for the editSectionLinkForOther function in linker
 178+ *
 179+ * @param $callobj Article object.
 180+ * @param $title Title object.
 181+ * @param $section Integer: section number.
 182+ * @param $hint Link String: title, or default if omitted or empty
 183+ * @param $url Link String: for edit url
 184+ * @param $result String: Returns the section [new][edit][reply] html if in a talk page - otherwise whatever came in with
 185+ * @return true
 186+ */
 187+
 188+ function efDiscussionLink4other ($callobj, $title, $section , $url , &$result)
 189+ {
 190+ if($wgSectionThreadingOn && $title->isTalkPage() ) {
 191+ $commenturl = '&section='.$section.'&replyto=yes';
 192+ $curl = $callobj->makeKnownLinkObj( $title, wfMsg('replysection'), 'action=edit'.$commenturl );
 193+ $newthreadurl = '&section=new';
 194+ $nurl = $callobj->makeKnownLinkObj( $nt, wfMsg('threadnewsection'), 'action=edit'.$newthreadurl );
 195+ $result = "<span class=\"editsection\">[".$nurl."][".$url."][".$curl."]</span>";
 196+ }
 197+ return (true);
 198+ }
 199+
 200+ /**
 201+ * This function creates a linkobject for the editSectionLink function in linker
 202+ *
 203+ * @param $callobj Article object.
 204+ * @param $nt Title object.
 205+ * @param $section Integer: section number.
 206+ * @param $hint Link String: title, or default if omitted or empty
 207+ * @param $url Link String: for edit url
 208+ * @param $result String: Returns the section [new][edit][reply] html if in a talk page - otherwise whatever came in with
 209+ * @return true
 210+ */
 211+
 212+ function efDiscussionLink ($callobj, $nt, $section, $hint='', $url , &$result)
 213+ {
 214+ global $wgSectionThreadingOn;
 215+ if($wgSectionThreadingOn && $nt->isTalkPage() ) {
 216+ $commenturl = '&section='.$section.'&replyto=yes';
 217+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'replysectionhint', htmlspecialchars( $hint ) ) . '"';
 218+ $curl = $callobj->makeKnownLinkObj( $nt, wfMsg('replysection'), 'action=edit'.$commenturl, '', '', '', $hint );
 219+ $newthreadurl = '&section=new';
 220+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'threadnewsectionhint', htmlspecialchars( $hint ) ) . '"';
 221+ $nurl = $callobj->makeKnownLinkObj( $nt, wfMsg('threadnewsection'), 'action=edit'.$newthreadurl, '', '', '', $hint );
 222+ $result = "<span class=\"editsection\">[".$nurl."][".$url."][".$curl."]</span>";
 223+ }
 224+ return (true);
 225+ }
 226+
 227+ /**
 228+ * This function is a hook used to test to see if empty, if so, start a comment
 229+ *
 230+ * @param $efform form object.
 231+ * @return true
 232+ */
 233+
 234+
 235+ function efDiscussionThreadEdit ($efform) {
 236+ global $wgRequest;
 237+ $efform->replytosection = '';
 238+ $efform->replyadded = false;
 239+ $efform->replytosection = $wgRequest->getVal( 'replyto' );
 240+ if( !$efform->mTitle->exists() ) {
 241+ if($wgSectionThreadingOn && $efform->mTitle->isTalkPage() ) {
 242+ $efform->section = 'new';
 243+ }
 244+ }
 245+ return (true);
 246+ }
 247+
 248+ /**
 249+ * Create a new header, one level below the 'replyto' header, add re: to front and tag it with user information
 250+ *
 251+ * @param $efform Form Object before display
 252+ * @return true
 253+ */
 254+
 255+ function efDiscussionThread($efform){
 256+ global $wgSectionThreadingOn;
 257+ $wgSectionThreadingOn = isset($wgSectionThreadingOn) ? $wgSectionThreadingOn : false;
 258+ if ( $efform->replytosection != '' && $wgSectionThreadingOn && !$efform->replyadded) {
 259+ if ($efform->replytosection != '') {
 260+ $text = $efform->textbox1;
 261+ $matches = array();
 262+ preg_match( "/^(=+)(.+)\\1/mi",
 263+ $efform->textbox1,
 264+ $matches );
 265+ if( !empty( $matches[2] ) ) {
 266+ preg_match( "/.*(-+)\\1/mi",$matches[2],$matchsign);
 267+ if (!empty($matchsign[0]) ){
 268+ $text = $text."\n\n".$matches[1]."=Re: ".trim($matchsign[0])." ~~~~".$matches[1]."=";
 269+ } else {
 270+ $text = $text."\n\n".$matches[1]."=Re: ".trim($matches[2])." -- ~~~~".$matches[1]."=";
 271+ }
 272+ } else {
 273+ $text = $text." -- ~~~~<br>\n\n";
 274+ }
 275+ $efform->replyadded = true;
 276+ $efform->textbox1 = $text;
 277+ }
 278+ return (true);
 279+ }
 280+ return (true);
 281+ }
 282+ /**
 283+ * When the new header is created from summary in new (+) add comment, just stamp the header as created
 284+ *
 285+ * @param $efform Form Object before display
 286+ * @return true
 287+ */
 288+
 289+ function efStampReply($efform){
 290+ global $wgSectionThreadingOn;
 291+ $wgSectionThreadingOn = isset($wgSectionThreadingOn) ? $wgSectionThreadingOn : false;
 292+ if ( $efform->section == "new" && $wgSectionThreadingOn && !$efform->replyadded) {
 293+ $efform->summary = $efform->summary." -- ~~~~";
 294+ }
 295+ return(true);
 296+ }
 297+}
 298+?>
 299+</pre>
 300+
 301+== Internationalisation file DiscussionThreading.i18n.php ==
 302+
 303+I was only completely comfortable with the english translations - looking to others to help me put the correct language translations in.
 304+
 305+I would deeply appreciate your help here.
 306+
 307+Load into file (in extension DiscussionThreading.i18n.php)
 308+
 309+<pre>
 310+
 311+<?php
 312+/**
 313+ * Internationalisation file for DiscussionThreading extension.
 314+ *
 315+ * @addtogroup Extensions
 316+*/
 317+
 318+$wgDiscussionThreadMessages = array();
 319+
 320+$wgDiscussionThreadMessages['en'] = array(
 321+ 'replysection' => 'reply',
 322+ 'replysectionhint' => "Reply to this Posting",
 323+ 'threadnewsection' => 'new',
 324+ 'threadnewsectionhint' => "Start a new thread"
 325+);
 326+
 327+/* Need to add
 328+ af,br,bs,ca,cs,cy,de,et,eu,fi,fr,ga,gl,he,hr,hsb,id,is,it,ja,kk-kz,kk-tr,kk-cn,kk,lv,nl,
 329+ no,nn,oc,pt,pt-br,ro,ru,sk,sl,sq,uk,wa,zh-cn,zh-tw,zh-yue,zh-hk,zh-sg
 330+
 331+ Would do this by adding a new $wgDiscussionThreadMessages array example:
 332+
 333+$wgDiscussionThreadMessages['lang1'] = array(
 334+ 'replysection' => 'lang1 - tag',
 335+ 'replysectionhint' => "lang1 - hint",
 336+ 'threadnewsection' => 'lang1 - new',
 337+ 'threadnewsectionhint' => "'lang1 - Start a new thread"
 338+);
 339+*/
 340+?>
 341+</pre>
 342+
 343+Would add new languages with a new array per, using this array schema for each language:
 344+
 345+<pre>
 346+$wgDiscussionThreadMessages['lang1'] = array(
 347+ 'replysection' => 'lang1 - tag',
 348+ 'replysectionhint' => "lang1 - hint",
 349+ 'threadnewsection' => 'lang1 - new',
 350+ 'threadnewsectionhint' => "'lang1 - Start a new thread"
 351+);
 352+</pre>
 353+
 354+== Indenting Headers ==
 355+
 356+If you are really serious about threading, you can indent headers by modifying the corresponding CSS file (eg. skins/monobook/main.css). To modify all headers (talk or regular article), you can add a 'padding-left: <small>''x''</small>em;' into each of the headers 3-6. Eg.:
 357+
 358+h3 { font-size: 132%; padding-left: <span style="color: red">1em</span>;}<br>
 359+h3 .editsection { font-size: 76%; font-weight: normal; }<br>
 360+h4 { font-size: 116%; padding-left: <span style="color: red">2em</span>;}<br>
 361+h4 .editsection { font-size: 86%; font-weight: normal; }<br>
 362+h5 { font-size: 100%; padding-left: <span style="color: red">3em</span>;}<br>
 363+h5 .editsection { font-weight: normal; }<br>
 364+h6 { font-size: 80%; padding-left: <span style="color: red">4em</span>; }<br>
 365+h6 .editsection { font-size: 125%; font-weight: normal; }<br>
 366+
 367+
 368+<noinclude>[[Category:Extensions]]</noinclude>
 369+[[category:Uncategorized extension]]
Index: trunk/extensions/DiscussionThreading/REV1_10_1/includes/Linker.php
@@ -0,0 +1,1224 @@
 2+<?php
 3+/**
 4+ * Split off some of the internal bits from Skin.php.
 5+ * These functions are used for primarily page content:
 6+ * links, embedded images, table of contents. Links are
 7+ * also used in the skin.
 8+ * For the moment, Skin is a descendent class of Linker.
 9+ * In the future, it should probably be further split
 10+ * so that ever other bit of the wiki doesn't have to
 11+ * go loading up Skin to get at it.
 12+ *
 13+ * @addtogroup Skins
 14+ */
 15+class Linker {
 16+ function __construct() {}
 17+
 18+ /**
 19+ * @deprecated
 20+ */
 21+ function postParseLinkColour( $s = NULL ) {
 22+ return NULL;
 23+ }
 24+
 25+ /** @todo document */
 26+ function getExternalLinkAttributes( $link, $text, $class='' ) {
 27+ $link = htmlspecialchars( $link );
 28+
 29+ $r = ($class != '') ? " class=\"$class\"" : " class=\"external\"";
 30+
 31+ $r .= " title=\"{$link}\"";
 32+ return $r;
 33+ }
 34+
 35+ function getInterwikiLinkAttributes( $link, $text, $class='' ) {
 36+ global $wgContLang;
 37+
 38+ $link = urldecode( $link );
 39+ $link = $wgContLang->checkTitleEncoding( $link );
 40+ $link = preg_replace( '/[\\x00-\\x1f]/', ' ', $link );
 41+ $link = htmlspecialchars( $link );
 42+
 43+ $r = ($class != '') ? " class=\"$class\"" : " class=\"external\"";
 44+
 45+ $r .= " title=\"{$link}\"";
 46+ return $r;
 47+ }
 48+
 49+ /** @todo document */
 50+ function getInternalLinkAttributes( $link, $text, $broken = false ) {
 51+ $link = urldecode( $link );
 52+ $link = str_replace( '_', ' ', $link );
 53+ $link = htmlspecialchars( $link );
 54+
 55+ if( $broken == 'stub' ) {
 56+ $r = ' class="stub"';
 57+ } else if ( $broken == 'yes' ) {
 58+ $r = ' class="new"';
 59+ } else {
 60+ $r = '';
 61+ }
 62+
 63+ $r .= " title=\"{$link}\"";
 64+ return $r;
 65+ }
 66+
 67+ /**
 68+ * @param $nt Title object.
 69+ * @param $text String: FIXME
 70+ * @param $broken Boolean: FIXME, default 'false'.
 71+ */
 72+ function getInternalLinkAttributesObj( &$nt, $text, $broken = false ) {
 73+ if( $broken == 'stub' ) {
 74+ $r = ' class="stub"';
 75+ } else if ( $broken == 'yes' ) {
 76+ $r = ' class="new"';
 77+ } else {
 78+ $r = '';
 79+ }
 80+
 81+ $r .= ' title="' . $nt->getEscapedText() . '"';
 82+ return $r;
 83+ }
 84+
 85+ /**
 86+ * This function is a shortcut to makeLinkObj(Title::newFromText($title),...). Do not call
 87+ * it if you already have a title object handy. See makeLinkObj for further documentation.
 88+ *
 89+ * @param $title String: the text of the title
 90+ * @param $text String: link text
 91+ * @param $query String: optional query part
 92+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 93+ * be included in the link text. Other characters will be appended after
 94+ * the end of the link.
 95+ */
 96+ function makeLink( $title, $text = '', $query = '', $trail = '' ) {
 97+ wfProfileIn( 'Linker::makeLink' );
 98+ $nt = Title::newFromText( $title );
 99+ if ($nt) {
 100+ $result = $this->makeLinkObj( Title::newFromText( $title ), $text, $query, $trail );
 101+ } else {
 102+ wfDebug( 'Invalid title passed to Linker::makeLink(): "'.$title."\"\n" );
 103+ $result = $text == "" ? $title : $text;
 104+ }
 105+
 106+ wfProfileOut( 'Linker::makeLink' );
 107+ return $result;
 108+ }
 109+
 110+ /**
 111+ * This function is a shortcut to makeKnownLinkObj(Title::newFromText($title),...). Do not call
 112+ * it if you already have a title object handy. See makeKnownLinkObj for further documentation.
 113+ *
 114+ * @param $title String: the text of the title
 115+ * @param $text String: link text
 116+ * @param $query String: optional query part
 117+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 118+ * be included in the link text. Other characters will be appended after
 119+ * the end of the link.
 120+ */
 121+ function makeKnownLink( $title, $text = '', $query = '', $trail = '', $prefix = '',$aprops = '') {
 122+ $nt = Title::newFromText( $title );
 123+ if ($nt) {
 124+ return $this->makeKnownLinkObj( Title::newFromText( $title ), $text, $query, $trail, $prefix , $aprops );
 125+ } else {
 126+ wfDebug( 'Invalid title passed to Linker::makeKnownLink(): "'.$title."\"\n" );
 127+ return $text == '' ? $title : $text;
 128+ }
 129+ }
 130+
 131+ /**
 132+ * This function is a shortcut to makeBrokenLinkObj(Title::newFromText($title),...). Do not call
 133+ * it if you already have a title object handy. See makeBrokenLinkObj for further documentation.
 134+ *
 135+ * @param string $title The text of the title
 136+ * @param string $text Link text
 137+ * @param string $query Optional query part
 138+ * @param string $trail Optional trail. Alphabetic characters at the start of this string will
 139+ * be included in the link text. Other characters will be appended after
 140+ * the end of the link.
 141+ */
 142+ function makeBrokenLink( $title, $text = '', $query = '', $trail = '' ) {
 143+ $nt = Title::newFromText( $title );
 144+ if ($nt) {
 145+ return $this->makeBrokenLinkObj( Title::newFromText( $title ), $text, $query, $trail );
 146+ } else {
 147+ wfDebug( 'Invalid title passed to Linker::makeBrokenLink(): "'.$title."\"\n" );
 148+ return $text == '' ? $title : $text;
 149+ }
 150+ }
 151+
 152+ /**
 153+ * This function is a shortcut to makeStubLinkObj(Title::newFromText($title),...). Do not call
 154+ * it if you already have a title object handy. See makeStubLinkObj for further documentation.
 155+ *
 156+ * @param $title String: the text of the title
 157+ * @param $text String: link text
 158+ * @param $query String: optional query part
 159+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 160+ * be included in the link text. Other characters will be appended after
 161+ * the end of the link.
 162+ */
 163+ function makeStubLink( $title, $text = '', $query = '', $trail = '' ) {
 164+ $nt = Title::newFromText( $title );
 165+ if ($nt) {
 166+ return $this->makeStubLinkObj( Title::newFromText( $title ), $text, $query, $trail );
 167+ } else {
 168+ wfDebug( 'Invalid title passed to Linker::makeStubLink(): "'.$title."\"\n" );
 169+ return $text == '' ? $title : $text;
 170+ }
 171+ }
 172+
 173+ /**
 174+ * Make a link for a title which may or may not be in the database. If you need to
 175+ * call this lots of times, pre-fill the link cache with a LinkBatch, otherwise each
 176+ * call to this will result in a DB query.
 177+ *
 178+ * @param $nt Title: the title object to make the link from, e.g. from
 179+ * Title::newFromText.
 180+ * @param $text String: link text
 181+ * @param $query String: optional query part
 182+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 183+ * be included in the link text. Other characters will be appended after
 184+ * the end of the link.
 185+ * @param $prefix String: optional prefix. As trail, only before instead of after.
 186+ */
 187+ function makeLinkObj( $nt, $text= '', $query = '', $trail = '', $prefix = '' ) {
 188+ global $wgUser;
 189+ $fname = 'Linker::makeLinkObj';
 190+ wfProfileIn( $fname );
 191+
 192+ # Fail gracefully
 193+ if ( ! is_object($nt) ) {
 194+ # throw new MWException();
 195+ wfProfileOut( $fname );
 196+ return "<!-- ERROR -->{$prefix}{$text}{$trail}";
 197+ }
 198+
 199+ if ( $nt->isExternal() ) {
 200+ $u = $nt->getFullURL();
 201+ $link = $nt->getPrefixedURL();
 202+ if ( '' == $text ) { $text = $nt->getPrefixedText(); }
 203+ $style = $this->getInterwikiLinkAttributes( $link, $text, 'extiw' );
 204+
 205+ $inside = '';
 206+ if ( '' != $trail ) {
 207+ $m = array();
 208+ if ( preg_match( '/^([a-z]+)(.*)$$/sD', $trail, $m ) ) {
 209+ $inside = $m[1];
 210+ $trail = $m[2];
 211+ }
 212+ }
 213+ $t = "<a href=\"{$u}\"{$style}>{$text}{$inside}</a>";
 214+
 215+ wfProfileOut( $fname );
 216+ return $t;
 217+ } elseif ( $nt->isAlwaysKnown() ) {
 218+ # Image links, special page links and self-links with fragements are always known.
 219+ $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
 220+ } else {
 221+ wfProfileIn( $fname.'-immediate' );
 222+ # Work out link colour immediately
 223+ $aid = $nt->getArticleID() ;
 224+ if ( 0 == $aid ) {
 225+ $retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix );
 226+ } else {
 227+ $threshold = $wgUser->getOption('stubthreshold') ;
 228+ if ( $threshold > 0 ) {
 229+ $dbr = wfGetDB( DB_SLAVE );
 230+ $s = $dbr->selectRow(
 231+ array( 'page' ),
 232+ array( 'page_len',
 233+ 'page_namespace',
 234+ 'page_is_redirect' ),
 235+ array( 'page_id' => $aid ), $fname ) ;
 236+ if ( $s !== false ) {
 237+ $size = $s->page_len;
 238+ if ( $s->page_is_redirect OR $s->page_namespace != NS_MAIN ) {
 239+ $size = $threshold*2 ; # Really big
 240+ }
 241+ } else {
 242+ $size = $threshold*2 ; # Really big
 243+ }
 244+ } else {
 245+ $size = 1 ;
 246+ }
 247+ if ( $size < $threshold ) {
 248+ $retVal = $this->makeStubLinkObj( $nt, $text, $query, $trail, $prefix );
 249+ } else {
 250+ $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
 251+ }
 252+ }
 253+ wfProfileOut( $fname.'-immediate' );
 254+ }
 255+ wfProfileOut( $fname );
 256+ return $retVal;
 257+ }
 258+
 259+ /**
 260+ * Make a link for a title which definitely exists. This is faster than makeLinkObj because
 261+ * it doesn't have to do a database query. It's also valid for interwiki titles and special
 262+ * pages.
 263+ *
 264+ * @param $nt Title object of target page
 265+ * @param $text String: text to replace the title
 266+ * @param $query String: link target
 267+ * @param $trail String: text after link
 268+ * @param $prefix String: text before link text
 269+ * @param $aprops String: extra attributes to the a-element
 270+ * @param $style String: style to apply - if empty, use getInternalLinkAttributesObj instead
 271+ * @return the a-element
 272+ */
 273+ function makeKnownLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) {
 274+
 275+ $fname = 'Linker::makeKnownLinkObj';
 276+ wfProfileIn( $fname );
 277+
 278+ if ( !is_object( $nt ) ) {
 279+ wfProfileOut( $fname );
 280+ return $text;
 281+ }
 282+
 283+ $u = $nt->escapeLocalURL( $query );
 284+ if ( $nt->getFragment() != '' ) {
 285+ if( $nt->getPrefixedDbkey() == '' ) {
 286+ $u = '';
 287+ if ( '' == $text ) {
 288+ $text = htmlspecialchars( $nt->getFragment() );
 289+ }
 290+ }
 291+ $u .= $nt->getFragmentForURL();
 292+ }
 293+ if ( $text == '' ) {
 294+ $text = htmlspecialchars( $nt->getPrefixedText() );
 295+ }
 296+ if ( $style == '' ) {
 297+ $style = $this->getInternalLinkAttributesObj( $nt, $text );
 298+ }
 299+
 300+ if ( $aprops !== '' ) $aprops = ' ' . $aprops;
 301+
 302+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 303+ $r = "<a href=\"{$u}\"{$style}{$aprops}>{$prefix}{$text}{$inside}</a>{$trail}";
 304+ wfProfileOut( $fname );
 305+ return $r;
 306+ }
 307+
 308+ /**
 309+ * Make a red link to the edit page of a given title.
 310+ *
 311+ * @param $title String: The text of the title
 312+ * @param $text String: Link text
 313+ * @param $query String: Optional query part
 314+ * @param $trail String: Optional trail. Alphabetic characters at the start of this string will
 315+ * be included in the link text. Other characters will be appended after
 316+ * the end of the link.
 317+ */
 318+ function makeBrokenLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 319+ # Fail gracefully
 320+ if ( ! isset($nt) ) {
 321+ # throw new MWException();
 322+ return "<!-- ERROR -->{$prefix}{$text}{$trail}";
 323+ }
 324+
 325+ $fname = 'Linker::makeBrokenLinkObj';
 326+ wfProfileIn( $fname );
 327+
 328+ if ( '' == $query ) {
 329+ $q = 'action=edit';
 330+ } else {
 331+ $q = 'action=edit&'.$query;
 332+ }
 333+ $u = $nt->escapeLocalURL( $q );
 334+
 335+ if ( '' == $text ) {
 336+ $text = htmlspecialchars( $nt->getPrefixedText() );
 337+ }
 338+ $style = $this->getInternalLinkAttributesObj( $nt, $text, "yes" );
 339+
 340+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 341+ $s = "<a href=\"{$u}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
 342+
 343+ wfProfileOut( $fname );
 344+ return $s;
 345+ }
 346+
 347+ /**
 348+ * Make a brown link to a short article.
 349+ *
 350+ * @param $title String: the text of the title
 351+ * @param $text String: link text
 352+ * @param $query String: optional query part
 353+ * @param $trail String: optional trail. Alphabetic characters at the start of this string will
 354+ * be included in the link text. Other characters will be appended after
 355+ * the end of the link.
 356+ */
 357+ function makeStubLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 358+ $style = $this->getInternalLinkAttributesObj( $nt, $text, 'stub' );
 359+ return $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix, '', $style );
 360+ }
 361+
 362+ /**
 363+ * Generate either a normal exists-style link or a stub link, depending
 364+ * on the given page size.
 365+ *
 366+ * @param $size Integer
 367+ * @param $nt Title object.
 368+ * @param $text String
 369+ * @param $query String
 370+ * @param $trail String
 371+ * @param $prefix String
 372+ * @return string HTML of link
 373+ */
 374+ function makeSizeLinkObj( $size, $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 375+ global $wgUser;
 376+ $threshold = intval( $wgUser->getOption( 'stubthreshold' ) );
 377+ if( $size < $threshold ) {
 378+ return $this->makeStubLinkObj( $nt, $text, $query, $trail, $prefix );
 379+ } else {
 380+ return $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
 381+ }
 382+ }
 383+
 384+ /**
 385+ * Make appropriate markup for a link to the current article. This is currently rendered
 386+ * as the bold link text. The calling sequence is the same as the other make*LinkObj functions,
 387+ * despite $query not being used.
 388+ */
 389+ function makeSelfLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 390+ if ( '' == $text ) {
 391+ $text = htmlspecialchars( $nt->getPrefixedText() );
 392+ }
 393+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 394+ return "<strong class=\"selflink\">{$prefix}{$text}{$inside}</strong>{$trail}";
 395+ }
 396+
 397+ /** @todo document */
 398+ function fnamePart( $url ) {
 399+ $basename = strrchr( $url, '/' );
 400+ if ( false === $basename ) {
 401+ $basename = $url;
 402+ } else {
 403+ $basename = substr( $basename, 1 );
 404+ }
 405+ return htmlspecialchars( $basename );
 406+ }
 407+
 408+ /** Obsolete alias */
 409+ function makeImage( $url, $alt = '' ) {
 410+ return $this->makeExternalImage( $url, $alt );
 411+ }
 412+
 413+ /** @todo document */
 414+ function makeExternalImage( $url, $alt = '' ) {
 415+ if ( '' == $alt ) {
 416+ $alt = $this->fnamePart( $url );
 417+ }
 418+ $s = '<img src="'.$url.'" alt="'.$alt.'" />';
 419+ return $s;
 420+ }
 421+
 422+ /** @todo document */
 423+ function makeImageLinkObj( $nt, $label, $alt, $align = '', $params = array(), $framed = false,
 424+ $thumb = false, $manual_thumb = '', $valign = '' )
 425+ {
 426+ global $wgContLang, $wgUser, $wgThumbLimits;
 427+
 428+ $img = new Image( $nt );
 429+
 430+ if ( !$img->allowInlineDisplay() && $img->exists() ) {
 431+ return $this->makeKnownLinkObj( $nt );
 432+ }
 433+
 434+ $error = $prefix = $postfix = '';
 435+ $page = isset( $params['page'] ) ? $params['page'] : false;
 436+
 437+ if ( 'center' == $align )
 438+ {
 439+ $prefix = '<div class="center">';
 440+ $postfix = '</div>';
 441+ $align = 'none';
 442+ }
 443+
 444+ if ( !isset( $params['width'] ) ) {
 445+ $params['width'] = $img->getWidth( $page );
 446+ if( $thumb || $framed ) {
 447+ $wopt = $wgUser->getOption( 'thumbsize' );
 448+
 449+ if( !isset( $wgThumbLimits[$wopt] ) ) {
 450+ $wopt = User::getDefaultOption( 'thumbsize' );
 451+ }
 452+
 453+ $params['width'] = min( $params['width'], $wgThumbLimits[$wopt] );
 454+ }
 455+ }
 456+
 457+ if ( $thumb || $framed ) {
 458+
 459+ # Create a thumbnail. Alignment depends on language
 460+ # writing direction, # right aligned for left-to-right-
 461+ # languages ("Western languages"), left-aligned
 462+ # for right-to-left-languages ("Semitic languages")
 463+ #
 464+ # If thumbnail width has not been provided, it is set
 465+ # to the default user option as specified in Language*.php
 466+ if ( $align == '' ) {
 467+ $align = $wgContLang->isRTL() ? 'left' : 'right';
 468+ }
 469+ return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $params, $framed, $manual_thumb ).$postfix;
 470+ }
 471+
 472+ if ( $params['width'] && $img->exists() ) {
 473+ # Create a resized image, without the additional thumbnail features
 474+ $thumb = $img->transform( $params );
 475+ } else {
 476+ $thumb = false;
 477+ }
 478+
 479+ if ( $page ) {
 480+ $query = 'page=' . urlencode( $page );
 481+ } else {
 482+ $query = '';
 483+ }
 484+ $u = $nt->getLocalURL( $query );
 485+ $imgAttribs = array(
 486+ 'alt' => $alt,
 487+ 'longdesc' => $u
 488+ );
 489+ if ( $valign ) {
 490+ $imgAttribs['style'] = "vertical-align: $valign";
 491+ }
 492+ $linkAttribs = array(
 493+ 'href' => $u,
 494+ 'class' => 'image',
 495+ 'title' => $alt
 496+ );
 497+
 498+ if ( !$thumb ) {
 499+ $s = $this->makeBrokenImageLinkObj( $img->getTitle() );
 500+ } else {
 501+ $s = $thumb->toHtml( $imgAttribs, $linkAttribs );
 502+ }
 503+ if ( '' != $align ) {
 504+ $s = "<div class=\"float{$align}\"><span>{$s}</span></div>";
 505+ }
 506+ return str_replace("\n", ' ',$prefix.$s.$postfix);
 507+ }
 508+
 509+ /**
 510+ * Make HTML for a thumbnail including image, border and caption
 511+ * $img is an Image object
 512+ */
 513+ function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manual_thumb = "" ) {
 514+ global $wgStylePath, $wgContLang;
 515+ $thumbUrl = '';
 516+ $error = '';
 517+
 518+ $page = isset( $params['page'] ) ? $params['page'] : false;
 519+
 520+ if ( empty( $params['width'] ) ) {
 521+ $params['width'] = 180;
 522+ }
 523+ $thumb = false;
 524+ if ( $manual_thumb != '' ) {
 525+ # Use manually specified thumbnail
 526+ $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb );
 527+ if( $manual_title ) {
 528+ $manual_img = new Image( $manual_title );
 529+ $thumb = $manual_img->getUnscaledThumb();
 530+ }
 531+ } elseif ( $framed ) {
 532+ // Use image dimensions, don't scale
 533+ $thumb = $img->getUnscaledThumb( $page );
 534+ } else {
 535+ $thumb = $img->transform( $params );
 536+ }
 537+
 538+ if ( $thumb ) {
 539+ $outerWidth = $thumb->getWidth() + 2;
 540+ } else {
 541+ $outerWidth = $params['width'] + 2;
 542+ }
 543+
 544+ $query = $page ? 'page=' . urlencode( $page ) : '';
 545+ $u = $img->getTitle()->getLocalURL( $query );
 546+
 547+ $more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
 548+ $magnifyalign = $wgContLang->isRTL() ? 'left' : 'right';
 549+ $textalign = $wgContLang->isRTL() ? ' style="text-align:right"' : '';
 550+
 551+ $s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
 552+ if ( !$thumb ) {
 553+ $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
 554+ $zoomicon = '';
 555+ } elseif( !$img->exists() ) {
 556+ $s .= $this->makeBrokenImageLinkObj( $img->getTitle() );
 557+ $zoomicon = '';
 558+ } else {
 559+ $imgAttribs = array(
 560+ 'alt' => $alt,
 561+ 'longdesc' => $u,
 562+ 'class' => 'thumbimage'
 563+ );
 564+ $linkAttribs = array(
 565+ 'href' => $u,
 566+ 'class' => 'internal',
 567+ 'title' => $alt
 568+ );
 569+
 570+ $s .= $thumb->toHtml( $imgAttribs, $linkAttribs );
 571+ if ( $framed ) {
 572+ $zoomicon="";
 573+ } else {
 574+ $zoomicon = '<div class="magnify" style="float:'.$magnifyalign.'">'.
 575+ '<a href="'.$u.'" class="internal" title="'.$more.'">'.
 576+ '<img src="'.$wgStylePath.'/common/images/magnify-clip.png" ' .
 577+ 'width="15" height="11" alt="" /></a></div>';
 578+ }
 579+ }
 580+ $s .= ' <div class="thumbcaption"'.$textalign.'>'.$zoomicon.$label."</div></div></div>";
 581+ return str_replace("\n", ' ', $s);
 582+ }
 583+
 584+ /**
 585+ * Pass a title object, not a title string
 586+ */
 587+ function makeBrokenImageLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
 588+ # Fail gracefully
 589+ if ( ! isset($nt) ) {
 590+ # throw new MWException();
 591+ return "<!-- ERROR -->{$prefix}{$text}{$trail}";
 592+ }
 593+
 594+ $fname = 'Linker::makeBrokenImageLinkObj';
 595+ wfProfileIn( $fname );
 596+
 597+ $q = 'wpDestFile=' . urlencode( $nt->getDBkey() );
 598+ if ( '' != $query ) {
 599+ $q .= "&$query";
 600+ }
 601+ $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
 602+ $url = $uploadTitle->escapeLocalURL( $q );
 603+
 604+ if ( '' == $text ) {
 605+ $text = htmlspecialchars( $nt->getPrefixedText() );
 606+ }
 607+ $style = $this->getInternalLinkAttributesObj( $nt, $text, "yes" );
 608+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 609+ $s = "<a href=\"{$url}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
 610+
 611+ wfProfileOut( $fname );
 612+ return $s;
 613+ }
 614+
 615+ /** @todo document */
 616+ function makeMediaLink( $name, /* wtf?! */ $url, $alt = '' ) {
 617+ $nt = Title::makeTitleSafe( NS_IMAGE, $name );
 618+ return $this->makeMediaLinkObj( $nt, $alt );
 619+ }
 620+
 621+ /**
 622+ * Create a direct link to a given uploaded file.
 623+ *
 624+ * @param $title Title object.
 625+ * @param $text String: pre-sanitized HTML
 626+ * @return string HTML
 627+ *
 628+ * @public
 629+ * @todo Handle invalid or missing images better.
 630+ */
 631+ function makeMediaLinkObj( $title, $text = '' ) {
 632+ if( is_null( $title ) ) {
 633+ ### HOTFIX. Instead of breaking, return empty string.
 634+ return $text;
 635+ } else {
 636+ $img = new Image( $title );
 637+ if( $img->exists() ) {
 638+ $url = $img->getURL();
 639+ $class = 'internal';
 640+ } else {
 641+ $upload = SpecialPage::getTitleFor( 'Upload' );
 642+ $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $img->getName() ) );
 643+ $class = 'new';
 644+ }
 645+ $alt = htmlspecialchars( $title->getText() );
 646+ if( $text == '' ) {
 647+ $text = $alt;
 648+ }
 649+ $u = htmlspecialchars( $url );
 650+ return "<a href=\"{$u}\" class=\"$class\" title=\"{$alt}\">{$text}</a>";
 651+ }
 652+ }
 653+
 654+ /** @todo document */
 655+ function specialLink( $name, $key = '' ) {
 656+ global $wgContLang;
 657+
 658+ if ( '' == $key ) { $key = strtolower( $name ); }
 659+ $pn = $wgContLang->ucfirst( $name );
 660+ return $this->makeKnownLink( $wgContLang->specialPage( $pn ),
 661+ wfMsg( $key ) );
 662+ }
 663+
 664+ /** @todo document */
 665+ function makeExternalLink( $url, $text, $escape = true, $linktype = '', $ns = null ) {
 666+ $style = $this->getExternalLinkAttributes( $url, $text, 'external ' . $linktype );
 667+ global $wgNoFollowLinks, $wgNoFollowNsExceptions;
 668+ if( $wgNoFollowLinks && !(isset($ns) && in_array($ns, $wgNoFollowNsExceptions)) ) {
 669+ $style .= ' rel="nofollow"';
 670+ }
 671+ $url = htmlspecialchars( $url );
 672+ if( $escape ) {
 673+ $text = htmlspecialchars( $text );
 674+ }
 675+ return '<a href="'.$url.'"'.$style.'>'.$text.'</a>';
 676+ }
 677+
 678+ /**
 679+ * Make user link (or user contributions for unregistered users)
 680+ * @param $userId Integer: user id in database.
 681+ * @param $userText String: user name in database
 682+ * @return string HTML fragment
 683+ * @private
 684+ */
 685+ function userLink( $userId, $userText ) {
 686+ $encName = htmlspecialchars( $userText );
 687+ if( $userId == 0 ) {
 688+ $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
 689+ return $this->makeKnownLinkObj( $contribsPage,
 690+ $encName);
 691+ } else {
 692+ $userPage = Title::makeTitle( NS_USER, $userText );
 693+ return $this->makeLinkObj( $userPage, $encName );
 694+ }
 695+ }
 696+
 697+ /**
 698+ * @param $userId Integer: user id in database.
 699+ * @param $userText String: user name in database.
 700+ * @param $redContribsWhenNoEdits Bool: return a red contribs link when the user had no edits and this is true.
 701+ * @return string HTML fragment with talk and/or block links
 702+ */
 703+ public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false ) {
 704+ global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
 705+ $talkable = !( $wgDisableAnonTalk && 0 == $userId );
 706+ $blockable = ( $wgSysopUserBans || 0 == $userId );
 707+
 708+ $items = array();
 709+ if( $talkable ) {
 710+ $items[] = $this->userTalkLink( $userId, $userText );
 711+ }
 712+ if( $userId ) {
 713+ // check if the user has an edit
 714+ if( $redContribsWhenNoEdits && User::edits( $userId ) == 0 ) {
 715+ $style = "class='new'";
 716+ } else {
 717+ $style = '';
 718+ }
 719+ $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
 720+
 721+ $items[] = $this->makeKnownLinkObj( $contribsPage, wfMsgHtml( 'contribslink' ), '', '', '', '', $style );
 722+ }
 723+ if( $blockable && $wgUser->isAllowed( 'block' ) ) {
 724+ $items[] = $this->blockLink( $userId, $userText );
 725+ }
 726+
 727+ if( $items ) {
 728+ return ' (' . implode( ' | ', $items ) . ')';
 729+ } else {
 730+ return '';
 731+ }
 732+ }
 733+
 734+ /**
 735+ * Alias for userToolLinks( $userId, $userText, true );
 736+ */
 737+ public function userToolLinksRedContribs( $userId, $userText ) {
 738+ return $this->userToolLinks( $userId, $userText, true );
 739+ }
 740+
 741+
 742+ /**
 743+ * @param $userId Integer: user id in database.
 744+ * @param $userText String: user name in database.
 745+ * @return string HTML fragment with user talk link
 746+ * @private
 747+ */
 748+ function userTalkLink( $userId, $userText ) {
 749+ $userTalkPage = Title::makeTitle( NS_USER_TALK, $userText );
 750+ $userTalkLink = $this->makeLinkObj( $userTalkPage, wfMsgHtml( 'talkpagelinktext' ) );
 751+ return $userTalkLink;
 752+ }
 753+
 754+ /**
 755+ * @param $userId Integer: userid
 756+ * @param $userText String: user name in database.
 757+ * @return string HTML fragment with block link
 758+ * @private
 759+ */
 760+ function blockLink( $userId, $userText ) {
 761+ $blockPage = SpecialPage::getTitleFor( 'Blockip', $userText );
 762+ $blockLink = $this->makeKnownLinkObj( $blockPage,
 763+ wfMsgHtml( 'blocklink' ) );
 764+ return $blockLink;
 765+ }
 766+
 767+ /**
 768+ * Generate a user link if the current user is allowed to view it
 769+ * @param $rev Revision object.
 770+ * @return string HTML
 771+ */
 772+ function revUserLink( $rev ) {
 773+ if( $rev->userCan( Revision::DELETED_USER ) ) {
 774+ $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() );
 775+ } else {
 776+ $link = wfMsgHtml( 'rev-deleted-user' );
 777+ }
 778+ if( $rev->isDeleted( Revision::DELETED_USER ) ) {
 779+ return '<span class="history-deleted">' . $link . '</span>';
 780+ }
 781+ return $link;
 782+ }
 783+
 784+ /**
 785+ * Generate a user tool link cluster if the current user is allowed to view it
 786+ * @param $rev Revision object.
 787+ * @return string HTML
 788+ */
 789+ function revUserTools( $rev ) {
 790+ if( $rev->userCan( Revision::DELETED_USER ) ) {
 791+ $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ) .
 792+ ' ' .
 793+ $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() );
 794+ } else {
 795+ $link = wfMsgHtml( 'rev-deleted-user' );
 796+ }
 797+ if( $rev->isDeleted( Revision::DELETED_USER ) ) {
 798+ return '<span class="history-deleted">' . $link . '</span>';
 799+ }
 800+ return $link;
 801+ }
 802+
 803+ /**
 804+ * This function is called by all recent changes variants, by the page history,
 805+ * and by the user contributions list. It is responsible for formatting edit
 806+ * comments. It escapes any HTML in the comment, but adds some CSS to format
 807+ * auto-generated comments (from section editing) and formats [[wikilinks]].
 808+ *
 809+ * @author Erik Moeller <moeller@scireview.de>
 810+ *
 811+ * Note: there's not always a title to pass to this function.
 812+ * Since you can't set a default parameter for a reference, I've turned it
 813+ * temporarily to a value pass. Should be adjusted further. --brion
 814+ *
 815+ * @param string $comment
 816+ * @param mixed $title Title object (to generate link to the section in autocomment) or null
 817+ * @param bool $local Whether section links should refer to local page
 818+ */
 819+ function formatComment($comment, $title = NULL, $local = false) {
 820+ wfProfileIn( __METHOD__ );
 821+
 822+ global $wgContLang;
 823+ $comment = str_replace( "\n", " ", $comment );
 824+ $comment = htmlspecialchars( $comment );
 825+
 826+ # The pattern for autogen comments is / * foo * /, which makes for
 827+ # some nasty regex.
 828+ # We look for all comments, match any text before and after the comment,
 829+ # add a separator where needed and format the comment itself with CSS
 830+ $match = array();
 831+ while (preg_match('/(.*)\/\*\s*(.*?)\s*\*\/(.*)/', $comment,$match)) {
 832+ $pre=$match[1];
 833+ $auto=$match[2];
 834+ $post=$match[3];
 835+ $link='';
 836+ if( $title ) {
 837+ $section = $auto;
 838+
 839+ # Generate a valid anchor name from the section title.
 840+ # Hackish, but should generally work - we strip wiki
 841+ # syntax, including the magic [[: that is used to
 842+ # "link rather than show" in case of images and
 843+ # interlanguage links.
 844+ $section = str_replace( '[[:', '', $section );
 845+ $section = str_replace( '[[', '', $section );
 846+ $section = str_replace( ']]', '', $section );
 847+ if ( $local ) {
 848+ $sectionTitle = Title::newFromText( '#' . $section);
 849+ } else {
 850+ $sectionTitle = wfClone( $title );
 851+ $sectionTitle->mFragment = $section;
 852+ }
 853+ $link = $this->makeKnownLinkObj( $sectionTitle, wfMsg( 'sectionlink' ) );
 854+ }
 855+ $sep='-';
 856+ $auto=$link.$auto;
 857+ if($pre) { $auto = $sep.' '.$auto; }
 858+ if($post) { $auto .= ' '.$sep; }
 859+ $auto='<span class="autocomment">'.$auto.'</span>';
 860+ $comment=$pre.$auto.$post;
 861+ }
 862+
 863+ # format regular and media links - all other wiki formatting
 864+ # is ignored
 865+ $medians = '(?:' . preg_quote( Namespace::getCanonicalName( NS_MEDIA ), '/' ) . '|';
 866+ $medians .= preg_quote( $wgContLang->getNsText( NS_MEDIA ), '/' ) . '):';
 867+ while(preg_match('/\[\[:?(.*?)(\|(.*?))*\]\](.*)$/',$comment,$match)) {
 868+ # Handle link renaming [[foo|text]] will show link as "text"
 869+ if( "" != $match[3] ) {
 870+ $text = $match[3];
 871+ } else {
 872+ $text = $match[1];
 873+ }
 874+ $submatch = array();
 875+ if( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) {
 876+ # Media link; trail not supported.
 877+ $linkRegexp = '/\[\[(.*?)\]\]/';
 878+ $thelink = $this->makeMediaLink( $submatch[1], "", $text );
 879+ } else {
 880+ # Other kind of link
 881+ if( preg_match( $wgContLang->linkTrail(), $match[4], $submatch ) ) {
 882+ $trail = $submatch[1];
 883+ } else {
 884+ $trail = "";
 885+ }
 886+ $linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/';
 887+ if (isset($match[1][0]) && $match[1][0] == ':')
 888+ $match[1] = substr($match[1], 1);
 889+ $thelink = $this->makeLink( $match[1], $text, "", $trail );
 890+ }
 891+ $comment = preg_replace( $linkRegexp, StringUtils::escapeRegexReplacement( $thelink ), $comment, 1 );
 892+ }
 893+ wfProfileOut( __METHOD__ );
 894+ return $comment;
 895+ }
 896+
 897+ /**
 898+ * Wrap a comment in standard punctuation and formatting if
 899+ * it's non-empty, otherwise return empty string.
 900+ *
 901+ * @param string $comment
 902+ * @param mixed $title Title object (to generate link to section in autocomment) or null
 903+ * @param bool $local Whether section links should refer to local page
 904+ *
 905+ * @return string
 906+ */
 907+ function commentBlock( $comment, $title = NULL, $local = false ) {
 908+ // '*' used to be the comment inserted by the software way back
 909+ // in antiquity in case none was provided, here for backwards
 910+ // compatability, acc. to brion -ævar
 911+ if( $comment == '' || $comment == '*' ) {
 912+ return '';
 913+ } else {
 914+ $formatted = $this->formatComment( $comment, $title, $local );
 915+ return " <span class=\"comment\">($formatted)</span>";
 916+ }
 917+ }
 918+
 919+ /**
 920+ * Wrap and format the given revision's comment block, if the current
 921+ * user is allowed to view it.
 922+ *
 923+ * @param Revision $rev
 924+ * @param bool $local Whether section links should refer to local page
 925+ * @return string HTML
 926+ */
 927+ function revComment( Revision $rev, $local = false ) {
 928+ if( $rev->userCan( Revision::DELETED_COMMENT ) ) {
 929+ $block = $this->commentBlock( $rev->getRawComment(), $rev->getTitle(), $local );
 930+ } else {
 931+ $block = " <span class=\"comment\">" .
 932+ wfMsgHtml( 'rev-deleted-comment' ) . "</span>";
 933+ }
 934+ if( $rev->isDeleted( Revision::DELETED_COMMENT ) ) {
 935+ return " <span class=\"history-deleted\">$block</span>";
 936+ }
 937+ return $block;
 938+ }
 939+
 940+ /** @todo document */
 941+ function tocIndent() {
 942+ return "\n<ul>";
 943+ }
 944+
 945+ /** @todo document */
 946+ function tocUnindent($level) {
 947+ return "</li>\n" . str_repeat( "</ul>\n</li>\n", $level>0 ? $level : 0 );
 948+ }
 949+
 950+ /**
 951+ * parameter level defines if we are on an indentation level
 952+ */
 953+ function tocLine( $anchor, $tocline, $tocnumber, $level ) {
 954+ return "\n<li class=\"toclevel-$level\"><a href=\"#" .
 955+ $anchor . '"><span class="tocnumber">' .
 956+ $tocnumber . '</span> <span class="toctext">' .
 957+ $tocline . '</span></a>';
 958+ }
 959+
 960+ /** @todo document */
 961+ function tocLineEnd() {
 962+ return "</li>\n";
 963+ }
 964+
 965+ /** @todo document */
 966+ function tocList($toc) {
 967+ global $wgJsMimeType;
 968+ $title = wfMsgHtml('toc') ;
 969+ return
 970+ '<table id="toc" class="toc" summary="' . $title .'"><tr><td>'
 971+ . '<div id="toctitle"><h2>' . $title . "</h2></div>\n"
 972+ . $toc
 973+ # no trailing newline, script should not be wrapped in a
 974+ # paragraph
 975+ . "</ul>\n</td></tr></table>"
 976+ . '<script type="' . $wgJsMimeType . '">'
 977+ . ' if (window.showTocToggle) {'
 978+ . ' var tocShowText = "' . wfEscapeJsString( wfMsg('showtoc') ) . '";'
 979+ . ' var tocHideText = "' . wfEscapeJsString( wfMsg('hidetoc') ) . '";'
 980+ . ' showTocToggle();'
 981+ . ' } '
 982+ . "</script>\n";
 983+ }
 984+
 985+ /** @todo document */
 986+ public function editSectionLinkForOther( $title, $section ) {
 987+ global $wgContLang;
 988+
 989+ $title = Title::newFromText( $title );
 990+ $editurl = '&section='.$section;
 991+ $url = $this->makeKnownLinkObj( $title, wfMsg('editsection'), 'action=edit'.$editurl );
 992+
 993+### - return "<span class=\"editsection\">[".$url."]</span>";
 994+ /** Added editSectionLinkForOther hook that allows section/header link to be modified */
 995+ $result = null;
 996+ wfRunHooks( 'EditSectionLinkForOther', array( &$this, $title, $section, $url, &$result ) );
 997+ return is_null( $result )
 998+ ? "<span class=\"editsection\">[{$url}]</span>"
 999+ : "<span class=\"editsection\">[{$result}]</span>";
 1000+ /** End of hook */
 1001+ }
 1002+
 1003+ /**
 1004+ * @param $title Title object.
 1005+ * @param $section Integer: section number.
 1006+ * @param $hint Link String: title, or default if omitted or empty
 1007+ */
 1008+ public function editSectionLink( $nt, $section, $hint='' ) {
 1009+ global $wgContLang;
 1010+
 1011+ $editurl = '&section='.$section;
 1012+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) ) . '"';
 1013+ $url = $this->makeKnownLinkObj( $nt, wfMsg('editsection'), 'action=edit'.$editurl, '', '', '', $hint );
 1014+
 1015+### - return "<span class=\"editsection\">[".$url."]</span>";
 1016+ /** Added editSectionLink hook that allows section/header link to be modified */
 1017+ $result = null;
 1018+ wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $hint, $url, &$result ) );
 1019+ return is_null( $result )
 1020+ ? "<span class=\"editsection\">[{$url}]</span>"
 1021+ : "<span class=\"editsection\">[{$result}]</span>";
 1022+ /** End of hook */
 1023+ }
 1024+
 1025+ /**
 1026+ * Create a headline for content
 1027+ *
 1028+ * @param int $level The level of the headline (1-6)
 1029+ * @param string $attribs Any attributes for the headline, starting with a space and ending with '>'
 1030+ * This *must* be at least '>' for no attribs
 1031+ * @param string $anchor The anchor to give the headline (the bit after the #)
 1032+ * @param string $text The text of the header
 1033+ * @param string $link HTML to add for the section edit link
 1034+ *
 1035+ * @return string HTML headline
 1036+ */
 1037+ public function makeHeadline( $level, $attribs, $anchor, $text, $link ) {
 1038+ return "<a name=\"$anchor\"></a><h$level$attribs$link <span class=\"mw-headline\">$text</span></h$level>";
 1039+ }
 1040+
 1041+ /**
 1042+ * Split a link trail, return the "inside" portion and the remainder of the trail
 1043+ * as a two-element array
 1044+ *
 1045+ * @static
 1046+ */
 1047+ static function splitTrail( $trail ) {
 1048+ static $regex = false;
 1049+ if ( $regex === false ) {
 1050+ global $wgContLang;
 1051+ $regex = $wgContLang->linkTrail();
 1052+ }
 1053+ $inside = '';
 1054+ if ( '' != $trail ) {
 1055+ $m = array();
 1056+ if ( preg_match( $regex, $trail, $m ) ) {
 1057+ $inside = $m[1];
 1058+ $trail = $m[2];
 1059+ }
 1060+ }
 1061+ return array( $inside, $trail );
 1062+ }
 1063+
 1064+ /**
 1065+ * Generate a rollback link for a given revision. Currently it's the
 1066+ * caller's responsibility to ensure that the revision is the top one. If
 1067+ * it's not, of course, the user will get an error message.
 1068+ *
 1069+ * If the calling page is called with the parameter &bot=1, all rollback
 1070+ * links also get that parameter. It causes the edit itself and the rollback
 1071+ * to be marked as "bot" edits. Bot edits are hidden by default from recent
 1072+ * changes, so this allows sysops to combat a busy vandal without bothering
 1073+ * other users.
 1074+ *
 1075+ * @param Revision $rev
 1076+ */
 1077+ function generateRollback( $rev ) {
 1078+ global $wgUser, $wgRequest;
 1079+ $title = $rev->getTitle();
 1080+
 1081+ $extraRollback = $wgRequest->getBool( 'bot' ) ? '&bot=1' : '';
 1082+ $extraRollback .= '&token=' . urlencode(
 1083+ $wgUser->editToken( array( $title->getPrefixedText(), $rev->getUserText() ) ) );
 1084+ return '<span class="mw-rollback-link">['. $this->makeKnownLinkObj( $title,
 1085+ wfMsg('rollbacklink'),
 1086+ 'action=rollback&from=' . urlencode( $rev->getUserText() ) . $extraRollback ) .']</span>';
 1087+ }
 1088+
 1089+ /**
 1090+ * Returns HTML for the "templates used on this page" list.
 1091+ *
 1092+ * @param array $templates Array of templates from Article::getUsedTemplate
 1093+ * or similar
 1094+ * @param bool $preview Whether this is for a preview
 1095+ * @param bool $section Whether this is for a section edit
 1096+ * @return string HTML output
 1097+ */
 1098+ public function formatTemplates( $templates, $preview = false, $section = false) {
 1099+ global $wgUser;
 1100+ wfProfileIn( __METHOD__ );
 1101+
 1102+ $sk = $wgUser->getSkin();
 1103+
 1104+ $outText = '';
 1105+ if ( count( $templates ) > 0 ) {
 1106+ # Do a batch existence check
 1107+ $batch = new LinkBatch;
 1108+ foreach( $templates as $title ) {
 1109+ $batch->addObj( $title );
 1110+ }
 1111+ $batch->execute();
 1112+
 1113+ # Construct the HTML
 1114+ $outText = '<div class="mw-templatesUsedExplanation">';
 1115+ if ( $preview ) {
 1116+ $outText .= wfMsgExt( 'templatesusedpreview', array( 'parse' ) );
 1117+ } elseif ( $section ) {
 1118+ $outText .= wfMsgExt( 'templatesusedsection', array( 'parse' ) );
 1119+ } else {
 1120+ $outText .= wfMsgExt( 'templatesused', array( 'parse' ) );
 1121+ }
 1122+ $outText .= '</div><ul>';
 1123+
 1124+ foreach ( $templates as $titleObj ) {
 1125+ $r = $titleObj->getRestrictions( 'edit' );
 1126+ if ( in_array( 'sysop', $r ) ) {
 1127+ $protected = wfMsgExt( 'template-protected', array( 'parseinline' ) );
 1128+ } elseif ( in_array( 'autoconfirmed', $r ) ) {
 1129+ $protected = wfMsgExt( 'template-semiprotected', array( 'parseinline' ) );
 1130+ } else {
 1131+ $protected = '';
 1132+ }
 1133+ $outText .= '<li>' . $sk->makeLinkObj( $titleObj ) . ' ' . $protected . '</li>';
 1134+ }
 1135+ $outText .= '</ul>';
 1136+ }
 1137+ wfProfileOut( __METHOD__ );
 1138+ return $outText;
 1139+ }
 1140+
 1141+ /**
 1142+ * Format a size in bytes for output, using an appropriate
 1143+ * unit (B, KB, MB or GB) according to the magnitude in question
 1144+ *
 1145+ * @param $size Size to format
 1146+ * @return string
 1147+ */
 1148+ public function formatSize( $size ) {
 1149+ global $wgLang;
 1150+ // For small sizes no decimal places necessary
 1151+ $round = 0;
 1152+ if( $size > 1024 ) {
 1153+ $size = $size / 1024;
 1154+ if( $size > 1024 ) {
 1155+ $size = $size / 1024;
 1156+ // For MB and bigger two decimal places are smarter
 1157+ $round = 2;
 1158+ if( $size > 1024 ) {
 1159+ $size = $size / 1024;
 1160+ $msg = 'size-gigabytes';
 1161+ } else {
 1162+ $msg = 'size-megabytes';
 1163+ }
 1164+ } else {
 1165+ $msg = 'size-kilobytes';
 1166+ }
 1167+ } else {
 1168+ $msg = 'size-bytes';
 1169+ }
 1170+ $size = round( $size, $round );
 1171+ return wfMsgHtml( $msg, $wgLang->formatNum( $size ) );
 1172+ }
 1173+
 1174+ /**
 1175+ * Given the id of an interface element, constructs the appropriate title
 1176+ * and accesskey attributes from the system messages. (Note, this is usu-
 1177+ * ally the id but isn't always, because sometimes the accesskey needs to
 1178+ * go on a different element than the id, for reverse-compatibility, etc.)
 1179+ *
 1180+ * @param string $name Id of the element, minus prefixes.
 1181+ * @return string title and accesskey attributes, ready to drop in an
 1182+ * element (e.g., ' title="This does something [x]" accesskey="x"').
 1183+ */
 1184+ public function tooltipAndAccesskey($name) {
 1185+ $out = '';
 1186+
 1187+ $tooltip = wfMsg('tooltip-'.$name);
 1188+ if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') {
 1189+ // Compatibility: formerly some tooltips had [alt-.] hardcoded
 1190+ $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
 1191+ $out .= ' title="'.htmlspecialchars($tooltip);
 1192+ }
 1193+ $accesskey = wfMsg('accesskey-'.$name);
 1194+ if ($accesskey && $accesskey != '-' && !wfEmptyMsg('accesskey-'.$name, $accesskey)) {
 1195+ if ($out) $out .= " [$accesskey]\" accesskey=\"$accesskey\"";
 1196+ else $out .= " title=\"[$accesskey]\" accesskey=\"$accesskey\"";
 1197+ } elseif ($out) {
 1198+ $out .= '"';
 1199+ }
 1200+ return $out;
 1201+ }
 1202+
 1203+ /**
 1204+ * Given the id of an interface element, constructs the appropriate title
 1205+ * attribute from the system messages. (Note, this is usually the id but
 1206+ * isn't always, because sometimes the accesskey needs to go on a different
 1207+ * element than the id, for reverse-compatibility, etc.)
 1208+ *
 1209+ * @param string $name Id of the element, minus prefixes.
 1210+ * @return string title attribute, ready to drop in an element
 1211+ * (e.g., ' title="This does something"').
 1212+ */
 1213+ public function tooltip($name) {
 1214+ $out = '';
 1215+
 1216+ $tooltip = wfMsg('tooltip-'.$name);
 1217+ if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') {
 1218+ $out = ' title="'.htmlspecialchars($tooltip).'"';
 1219+ }
 1220+
 1221+ return $out;
 1222+ }
 1223+}
 1224+
 1225+?>
Index: trunk/extensions/DiscussionThreading/REV1_10_1/DiscussionThreading1_10_1.patch
@@ -0,0 +1,37 @@
 2+Index: Linker.php
 3+===================================================================
 4+--- Linker.php (revision 25540)
 5+@@ -989,8 +989,14 @@
 6+ $editurl = '&section='.$section;
 7+ $url = $this->makeKnownLinkObj( $title, wfMsg('editsection'), 'action=edit'.$editurl );
 8+
 9+- return "<span class=\"editsection\">[".$url."]</span>";
 10+-
 11++### - return "<span class=\"editsection\">[".$url."]</span>";
 12++ /** Added editSectionLinkForOther hook that allows section/header link to be modified */
 13++ $result = null;
 14++ wfRunHooks( 'EditSectionLinkForOther', array( &$this, $title, $section, $url, &$result ) );
 15++ return is_null( $result )
 16++ ? "<span class=\"editsection\">[{$url}]</span>"
 17++ : "<span class=\"editsection\">[{$result}]</span>";
 18++ /** End of hook */
 19+ }
 20+
 21+ /**
 22+@@ -1005,7 +1011,14 @@
 23+ $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) ) . '"';
 24+ $url = $this->makeKnownLinkObj( $nt, wfMsg('editsection'), 'action=edit'.$editurl, '', '', '', $hint );
 25+
 26+- return "<span class=\"editsection\">[".$url."]</span>";
 27++### - return "<span class=\"editsection\">[".$url."]</span>";
 28++ /** Added editSectionLink hook that allows section/header link to be modified */
 29++ $result = null;
 30++ wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $hint, $url, &$result ) );
 31++ return is_null( $result )
 32++ ? "<span class=\"editsection\">[{$url}]</span>"
 33++ : "<span class=\"editsection\">[{$result}]</span>";
 34++ /** End of hook */
 35+ }
 36+
 37+ /**