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 = '§ion='.$section.'&replyto=yes'; |
| 72 | + $curl = $callobj->makeKnownLinkObj( $title, wfMsg('replysection'), 'action=edit'.$commenturl ); |
| 73 | + $newthreadurl = '§ion=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 = '§ion='.$section.'&replyto=yes'; |
| 97 | + $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'replysectionhint', htmlspecialchars( $hint ) ) . '"'; |
| 98 | + $curl = $callobj->makeKnownLinkObj( $nt, wfMsg('replysection'), 'action=edit'.$commenturl, '', '', '', $hint ); |
| 99 | + $newthreadurl = '§ion=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 = '§ion='.$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 = '§ion='.$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 = '§ion='.$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 = '§ion='.$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 = '§ion='.$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 = '§ion='.$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 = '§ion='.$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 = '§ion='.$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 = '§ion='.$section.'&replyto=yes'; |
| 192 | + $curl = $callobj->makeKnownLinkObj( $title, wfMsg('replysection'), 'action=edit'.$commenturl ); |
| 193 | + $newthreadurl = '§ion=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 = '§ion='.$section.'&replyto=yes'; |
| 217 | + $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'replysectionhint', htmlspecialchars( $hint ) ) . '"'; |
| 218 | + $curl = $callobj->makeKnownLinkObj( $nt, wfMsg('replysection'), 'action=edit'.$commenturl, '', '', '', $hint ); |
| 219 | + $newthreadurl = '§ion=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 = '§ion='.$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 = '§ion='.$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 = '§ion='.$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 | + /** |