From: Hashar Date: Fri, 23 Mar 2012 21:23:38 +0000 (+0000) Subject: Merge "http->https" X-Git-Tag: 1.31.0-rc.0~24142 X-Git-Url: https://git.cyclocoop.org/%27.WWW_URL.%27admin/?a=commitdiff_plain;h=c64dbc491d93eb7fa00bc6763021aa608d2f3815;hp=ab32cccd55018d4a85a428ea317f3146b65b3ffb;p=lhc%2Fweb%2Fwiklou.git Merge "http->https" --- diff --git a/.gitignore b/.gitignore index 6688707225..943378aec4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ cache/*.cdb images/[0-9a-f] images/temp images/thumb +maintenance/dev/data/ +maintenance/.mweval_history \ No newline at end of file diff --git a/RELEASE-NOTES-1.19 b/RELEASE-NOTES-1.19 index 576cfa8422..7ba2c8387f 100644 --- a/RELEASE-NOTES-1.19 +++ b/RELEASE-NOTES-1.19 @@ -13,15 +13,15 @@ production. === Changes since 1.19 beta 1 === * (bug 35014) Including a special page no longer sets the page's title to the - included page -* (bug 35019) Edit summaries are no longer transformed in notification e-mails -* (bug 35152) Help message for e-mail is shown again in user preferences + included page. +* (bug 35019) Edit summaries are no longer transformed in notification e-mails. +* (bug 35152) Help message for e-mail is shown again in user preferences. * (bug 34887) $3 and $4 parameters are now substituted correctly in message - "movepage-moved" + "movepage-moved". * (bug 34841) Edit links are no longer displayed when display old page versions -* (bug 34889) User name should be normalized on Special:Contributions -* (bug 35051) If heading has a trailing space after == then its name is not - preloaded into edit summary on section edit +* (bug 34889) User name should be normalized on Special:Contributions. +* (bug 35051) If heading has a trailing space after == then its name is not + preloaded into edit summary on section edit. * (bug 31417) New ID mw-content-text around the actual page text, without categories, contentSub, ... The same div often also contains the class mw-content-ltr/rtl. * (bug 35303) Proxy and DNS blacklist blocking works again @@ -31,6 +31,8 @@ production. core parser functions which operate on strings, such as padleft. * (bug 18295) Don't expose strip markers when a tag appears inside a link inside a heading. +* (bug 34907) Fixed exposure of tokens through load.php that could have facilitated + CSRF attacks === Configuration changes in 1.19 === * Removed SkinTemplateSetupPageCss hook; use BeforePageDisplay instead. @@ -45,6 +47,7 @@ production. * (bug 32239) Removed $wgEnableTooltipsAndAccesskeys. * Removed $wgVectorShowVariantName. * Removed $wgExtensionAliasesFiles. Use $wgExtensionMessagesFiles. +* Removed $wgResourceLoaderInlinePrivateModules, now always enabled. === New features in 1.19 === * (bug 19838) Add ability to get all interwiki prefixes also if the interwiki @@ -128,8 +131,8 @@ production. 200 status code instead of 404 for nonexistent articles. * (bug 33447) Link to the broken image tracking category from Special:Wantedfiles. * (bug 27724) Add timestamp to job queue. -* (bug 30339) Implement SpecialPage for running javascript tests. Disabled by default, due to - tests potentially being harmful, not to be run on a production wiki. +* (bug 30339) Implement SpecialPage for running javascript tests. Disabled by default, + due to tests potentially being harmful, not to be run on a production wiki. Enable by setting $wgEnableJavaScriptTest to true. * Extensions can use the RequestContextCreateSkin hook to override what skin is loaded in some contexts. @@ -146,8 +149,8 @@ production. * Special:MovePage now has a dropdown menu for namespaces. * (bug 34420) Special:Version now shows git HEAD sha1 when available. * (bug 33952) Refactor mw.toolbar to allow dynamic additions at any time. -* Now possible to specify separate section title and edit summary when adding a new section to a - page via the edit API action. +* Now possible to specify separate section title and edit summary when adding + a new section to a page via the edit API action. === Bug fixes in 1.19 === * $wgUploadNavigationUrl should be used for file redlinks if. diff --git a/api.php b/api.php index a5a257992d..889c5f10a2 100644 --- a/api.php +++ b/api.php @@ -45,7 +45,7 @@ if ( !function_exists( 'version_compare' ) || version_compare( phpversion(), '5. // Initialise common code. if ( isset( $_SERVER['MW_COMPILED'] ) ) { - require ( 'phase3/includes/WebStart.php' ); + require ( 'core/includes/WebStart.php' ); } else { require ( dirname( __FILE__ ) . '/includes/WebStart.php' ); } diff --git a/extensions/.gitignore b/extensions/.gitignore new file mode 100644 index 0000000000..f8476208f6 --- /dev/null +++ b/extensions/.gitignore @@ -0,0 +1,3 @@ +* +!README +!.gitignore diff --git a/img_auth.php b/img_auth.php index 3999bf3e80..82afef27bc 100644 --- a/img_auth.php +++ b/img_auth.php @@ -28,7 +28,7 @@ define( 'MW_NO_OUTPUT_COMPRESSION', 1 ); if ( isset( $_SERVER['MW_COMPILED'] ) ) { - require ( 'phase3/includes/WebStart.php' ); + require ( 'core/includes/WebStart.php' ); } else { require ( dirname( __FILE__ ) . '/includes/WebStart.php' ); } diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 4ab5de868b..e7918730d7 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -95,6 +95,7 @@ $wgAutoloadLocalClasses = array( 'FormAction' => 'includes/Action.php', 'FormOptions' => 'includes/FormOptions.php', 'FormSpecialPage' => 'includes/SpecialPage.php', + 'GitInfo' => 'includes/GitInfo.php', 'HashtableReplacer' => 'includes/StringUtils.php', 'HistoryBlob' => 'includes/HistoryBlob.php', 'HistoryBlobCurStub' => 'includes/HistoryBlob.php', diff --git a/includes/CryptRand.php b/includes/CryptRand.php index 10f379cb2a..e4be1b3788 100644 --- a/includes/CryptRand.php +++ b/includes/CryptRand.php @@ -120,7 +120,7 @@ class MWCryptRand { /** * Randomly hash data while mixing in clock drift data for randomness * - * @param $data The data to randomly hash. + * @param $data string The data to randomly hash. * @return String The hashed bytes * @author Tim Starling */ @@ -166,7 +166,7 @@ class MWCryptRand { /** * Return a rolling random state initially build using data from unstable sources - * @return A new weak random state + * @return string A new weak random state */ protected function randomState() { static $state = null; @@ -184,6 +184,7 @@ class MWCryptRand { /** * Decide on the best acceptable hash algorithm we have available for hash() + * @throws MWException * @return String A hash algorithm */ protected function hashAlgo() { @@ -227,6 +228,7 @@ class MWCryptRand { * Generate an acceptably unstable one-way-hash of some text * making use of the best hash algorithm that we have available. * + * @param $data string * @return String A raw hash of the data */ protected function hash( $data ) { @@ -237,6 +239,8 @@ class MWCryptRand { * Generate an acceptably unstable one-way-hmac of some text * making use of the best hash algorithm that we have available. * + * @param $data string + * @param $key string * @return String A raw hash of the data */ protected function hmac( $data, $key ) { @@ -282,7 +286,7 @@ class MWCryptRand { if ( $iv === false ) { wfDebug( __METHOD__ . ": mcrypt_create_iv returned false.\n" ); } else { - $bytes .= $iv; + $buffer .= $iv; wfDebug( __METHOD__ . ": mcrypt_create_iv generated " . strlen( $iv ) . " bytes of randomness.\n" ); } wfProfileOut( __METHOD__ . '-mcrypt' ); @@ -409,6 +413,7 @@ class MWCryptRand { /** * Return a singleton instance of MWCryptRand + * @return MWCryptRand */ protected static function singleton() { if ( is_null( self::$singleton ) ) { diff --git a/includes/GitInfo.php b/includes/GitInfo.php new file mode 100644 index 0000000000..bc3f35e3d1 --- /dev/null +++ b/includes/GitInfo.php @@ -0,0 +1,122 @@ +basedir = "{$dir}/.git/"; + } + + /** + * Return a singleton for the repo at $IP + * @return GitInfo + */ + public static function repo() { + global $IP; + if ( is_null( self::$repo ) ) { + self::$repo = new self( $IP ); + } + return self::$repo; + } + + /** + * Check if a string looks like a hex encoded SHA1 hash + * + * @param $str The string to check + * @return bool Whether or not the string looks like a SHA1 + */ + public static function isSHA1( $str ) { + return !!preg_match( '/^[0-9A-Z]{40}$/i', $str ); + } + + /** + * Return the HEAD of the repo (without any opening "ref: ") + * @return string The HEAD + */ + public function getHead() { + $HEADfile = "{$this->basedir}/HEAD"; + + if ( !is_readable( $HEADfile ) ) { + return false; + } + + $HEAD = file_get_contents( $HEADfile ); + + if ( preg_match( "/ref: (.*)/", $HEAD, $m ) ) { + return rtrim( $m[1] ); + } else { + return $HEAD; + } + } + + /** + * Return the SHA1 for the current HEAD of the repo + * @return string A SHA1 or false + */ + public function getHeadSHA1() { + $HEAD = $this->getHead(); + + // If detached HEAD may be a SHA1 + if ( self::isSHA1( $HEAD ) ) { + return $HEAD; + } + + // If not a SHA1 it may be a ref: + $REFfile = "{$this->basedir}{$HEAD}"; + if ( !is_readable( $REFfile ) ) { + return false; + } + + $sha1 = rtrim( file_get_contents( $REFfile ) ); + + return $sha1; + } + + /** + * Return the name of the current branch, or HEAD if not found + * @return string The branch name, HEAD, or false + */ + public function getCurrentBranch() { + $HEAD = $this->getHead(); + if ( $HEAD && preg_match( "#^refs/heads/(.*)$#", $HEAD, $m ) ) { + return $m[1]; + } else { + return $HEAD; + } + } + + /** + * @see self::getHeadSHA1 + */ + public static function headSHA1() { + return self::repo()->getHeadSHA1(); + } + + /** + * @see self::getCurrentBranch + */ + public static function currentBranch() { + return self::repo()->getCurrentBranch(); + } + +} \ No newline at end of file diff --git a/includes/specials/SpecialNewimages.php b/includes/specials/SpecialNewimages.php index b88123dc79..45dbd36d6b 100644 --- a/includes/specials/SpecialNewimages.php +++ b/includes/specials/SpecialNewimages.php @@ -123,7 +123,7 @@ class NewFilesPager extends ReverseChronologicalPager { $this->gallery->add( $title, "$ul
\n" - . htmlspecialchars( $this->getLanguage()->timeanddate( $row->img_timestamp, true ) ) + . htmlspecialchars( $this->getLanguage()->userTimeAndDate( $row->img_timestamp, $this->getUser() ) ) . "
\n" ); } @@ -139,7 +139,7 @@ class NewFilesPager extends ReverseChronologicalPager { ), 'showbots' => array( 'type' => 'check', - 'label' => wfMessage( 'showhidebots', wfMsg( 'show' ) ), + 'label' => $this->msg( 'showhidebots', $this->msg( 'show' )->plain() )->escaped(), 'name' => 'showbots', # 'default' => $this->getRequest()->getBool( 'showbots', 0 ), ), @@ -161,9 +161,9 @@ class NewFilesPager extends ReverseChronologicalPager { $form = new HTMLForm( $fields, $this->getContext() ); $form->setTitle( $this->getTitle() ); - $form->setSubmitText( wfMsg( 'ilsubmit' ) ); + $form->setSubmitTextMsg( 'ilsubmit' ); $form->setMethod( 'get' ); - $form->setWrapperLegend( wfMsg( 'newimages-legend' ) ); + $form->setWrapperLegendMsg( 'newimages-legend' ); return $form; } diff --git a/includes/specials/SpecialVersion.php b/includes/specials/SpecialVersion.php index 2b44337bb6..9181de0b75 100644 --- a/includes/specials/SpecialVersion.php +++ b/includes/specials/SpecialVersion.php @@ -160,10 +160,14 @@ class SpecialVersion extends SpecialPage { global $wgVersion, $IP; wfProfileIn( __METHOD__ ); - $info = self::getSvnInfo( $IP ); - if ( !$info ) { + $gitInfo = self::getGitHeadSha1( $IP ); + $svnInfo = self::getSvnInfo( $IP ); + if ( !$svnInfo && !$gitInfo ) { $version = $wgVersion; - } elseif( $flags === 'nodb' ) { + } elseif ( $gitInfo ) { + $shortSha1 = substr( $gitInfo, 0, 7 ); + $version = "$wgVersion ($shortSha1)"; + } elseif ( $flags === 'nodb' ) { $version = "$wgVersion (r{$info['checkout-rev']})"; } else { $version = $wgVersion . ' ' . @@ -717,24 +721,8 @@ class SpecialVersion extends SpecialPage { * @return bool|String sha1 of commit HEAD points to */ public static function getGitHeadSha1( $dir ) { - $BASEDIR = "{$dir}/.git/"; - $HEADfile = "{$BASEDIR}/HEAD"; - - if( !file_exists( $HEADfile ) ) { - return false; - } - - preg_match( "/ref: (.*)/", - file_get_contents( $HEADfile ), $m ); - - $REFfile = "{$BASEDIR}{$m[1]}"; - if( !file_exists( $REFfile ) ) { - return false; - } - - $sha1 = rtrim( file_get_contents( $REFfile ) ); - - return $sha1; + $repo = new GitInfo( $dir ); + return $repo->getHeadSHA1(); } function showEasterEgg() { diff --git a/mw-config/index.php b/mw-config/index.php index c65be69c0c..edfae92828 100644 --- a/mw-config/index.php +++ b/mw-config/index.php @@ -10,7 +10,7 @@ define( 'MEDIAWIKI_INSTALL', true ); chdir( dirname( dirname( __FILE__ ) ) ); if ( isset( $_SERVER['MW_COMPILED'] ) ) { - require ( 'phase3/includes/WebStart.php' ); + require ( 'core/includes/WebStart.php' ); } else { require( dirname( dirname( __FILE__ ) ) . '/includes/WebStart.php' ); } diff --git a/profileinfo.php b/profileinfo.php index 1549349bfd..ef038c1153 100644 --- a/profileinfo.php +++ b/profileinfo.php @@ -29,7 +29,7 @@ ini_set( 'zlib.output_compression', 'off' ); $wgEnableProfileInfo = $wgProfileToDatabase = false; if ( isset( $_SERVER['MW_COMPILED'] ) ) { - require ( 'phase3/includes/WebStart.php' ); + require ( 'core/includes/WebStart.php' ); } else { require ( dirname( __FILE__ ) . '/includes/WebStart.php' ); } @@ -56,16 +56,20 @@ header( 'Content-Type: text/html; charset=utf-8' ); text-align: right; } td.timep, td.tpc, td.tpr { - background-color: #fffff0; + background-color: #ffff80; } td.memoryp, td.mpc, td.mpr { - background-color: #f0f8ff; + background-color: #80f8ff; } td.count, td,cpr { - background-color: #f0fff0; + background-color: #80ff80; } td.name { - background-color: #f9f9f9; + background-color: #89f9f9; + } + + tr:hover { + font-weight: bold; } diff --git a/resources/mediawiki.action/mediawiki.action.watch.ajax.js b/resources/mediawiki.action/mediawiki.action.watch.ajax.js index f5f09f52a1..090e4c3d1e 100644 --- a/resources/mediawiki.action/mediawiki.action.watch.ajax.js +++ b/resources/mediawiki.action/mediawiki.action.watch.ajax.js @@ -4,152 +4,158 @@ */ ( function ( $, mw, undefined ) { -/** - * The name of the page to watch or unwatch. - */ -var title = mw.config.get( 'wgRelevantPageName', mw.config.get( 'wgPageName' ) ); - -/** - * Update the link text, link href attribute and (if applicable) - * "loading" class. - * - * @param $link {jQuery} Anchor tag of (un)watch link - * @param action {String} One of 'watch', 'unwatch'. - * @param state {String} [optional] 'idle' or 'loading'. Default is 'idle'. - */ -function updateWatchLink( $link, action, state ) { - // message keys 'watch', 'watching', 'unwatch' or 'unwatching'. - var msgKey = state === 'loading' ? action + 'ing' : action, - accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp ), + /** + * The name of the page to watch or unwatch. + */ + var title = mw.config.get( 'wgRelevantPageName', mw.config.get( 'wgPageName' ) ); + + /** + * Update the link text, link href attribute and (if applicable) + * "loading" class. + * + * @param $link {jQuery} Anchor tag of (un)watch link. + * @param action {String} One of 'watch', 'unwatch'. + * @param state {String} [optional] 'idle' or 'loading'. Default is 'idle'. + */ + function updateWatchLink( $link, action, state ) { + var accesskeyTip, msgKey, $li; + + // message keys 'watch', 'watching', 'unwatch' or 'unwatching'. + msgKey = state === 'loading' ? action + 'ing' : action; + accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp ); $li = $link.closest( 'li' ); - $link - .text( mw.msg( msgKey ) ) - .attr( 'title', mw.msg( 'tooltip-ca-' + action ) + - ( accesskeyTip ? ' ' + accesskeyTip[0] : '' ) - ) - .attr( 'href', mw.util.wikiScript() + '?' + $.param({ - title: title, - action: action - }) - ); - - // Special case for vector icon - if ( $li.hasClass( 'icon' ) ) { - if ( state === 'loading' ) { - $link.addClass( 'loading' ); - } else { - $link.removeClass( 'loading' ); + $link + .text( mw.msg( msgKey ) ) + .attr( 'title', mw.msg( 'tooltip-ca-' + action ) + + ( accesskeyTip ? ' ' + accesskeyTip[0] : '' ) + ) + .attr( 'href', mw.util.wikiScript() + '?' + $.param({ + title: title, + action: action + }) + ); + + // Special case for vector icon + if ( $li.hasClass( 'icon' ) ) { + if ( state === 'loading' ) { + $link.addClass( 'loading' ); + } else { + $link.removeClass( 'loading' ); + } } } -} -/** - * @todo This should be moved somewhere more accessible. - * @param url {String} - * @return {String} The extracted action, defaults to 'view'. - */ -function mwUriGetAction( url ) { - var actionPaths = mw.config.get( 'wgActionPaths' ), - key, parts, m, action; - - // @todo: Does MediaWiki give action path or query param - // precedence ? If the former, move this to the bottom - action = mw.util.getParamValue( 'action', url ); - if ( action !== null ) { - return action; - } + /** + * @todo This should be moved somewhere more accessible. + * @param url {String} + * @return {String} The extracted action, defaults to 'view'. + */ + function mwUriGetAction( url ) { + var action, actionPaths, key, i, m, parts; + + actionPaths = mw.config.get( 'wgActionPaths' ); + + // @todo: Does MediaWiki give action path or query param + // precedence ? If the former, move this to the bottom + action = mw.util.getParamValue( 'action', url ); + if ( action !== null ) { + return action; + } + + for ( key in actionPaths ) { + if ( actionPaths.hasOwnProperty( key ) ) { + parts = actionPaths[key].split( '$1' ); + for ( i = 0; i < parts.length; i += 1 ) { + parts[i] = $.escapeRE( parts[i] ); + } + m = new RegExp( parts.join( '(.+)' ) ).exec( url ); + if ( m && m[1] ) { + return key; + } - for ( key in actionPaths ) { - if ( actionPaths.hasOwnProperty( key ) ) { - parts = actionPaths[key].split( '$1' ); - for ( i = 0; i < parts.length; i += 1 ) { - parts[i] = $.escapeRE( parts[i] ); - } - m = new RegExp( parts.join( '(.+)' ) ).exec( url ); - if ( m && m[1] ) { - return key; } - } + + return 'view'; } - return 'view'; -} + $( document ).ready( function () { + var $links = $( '.mw-watchlink a, a.mw-watchlink, ' + + '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' + + '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' ); -$( document ).ready( function() { - var $links = $( '.mw-watchlink a, a.mw-watchlink, ' + - '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' + - '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' ); + // Allowing people to add inline animated links is a little scary + $links = $links.filter( ':not( #bodyContent *, #content * )' ); - // Allowing people to add inline animated links is a little scary - $links = $links.filter( ':not( #bodyContent *, #content * )' ); + $links.click( function ( e ) { + var action, api, $link; - $links.click( function( e ) { - var $link, api, action = mwUriGetAction( this.href ); - if ( action !== 'watch' && action !== 'unwatch' ) { - // Could not extract target action from link url, - // let native browsing handle it further - return true; - } - e.preventDefault(); - e.stopPropagation(); - - $link = $( this ); - - updateWatchLink( $link, action, 'loading' ); - - api = new mw.Api(); - api[action]( - title, - // Success - function( watchResponse ) { - var otherAction = action === 'watch' ? 'unwatch' : 'watch', - $li = $link.closest( 'li' ); + if ( action !== 'watch' && action !== 'unwatch' ) { + // Could not extract target action from link url, + // let native browsing handle it further + return true; + } + e.preventDefault(); + e.stopPropagation(); - mw.util.jsMessage( watchResponse.message, 'ajaxwatch' ); + $link = $( this ); - // Set link to opposite - updateWatchLink( $link, otherAction ); + updateWatchLink( $link, action, 'loading' ); - // Most common ID style - if ( $li.prop( 'id' ) === 'ca-' + otherAction || $li.prop( 'id' ) === 'ca-' + action ) { - $li.prop( 'id', 'ca-' + otherAction ); - } - - // Bug 12395 - update the watch checkbox on edit pages when the - // page is watched or unwatched via the tab. - if ( watchResponse.watched !== undefined ) { - $( '#wpWatchthis' ).prop( 'checked', true ); - } else { - $( '#wpWatchthis' ).removeProp( 'checked' ); - } - }, - // Error - function(){ - - // Reset link to non-loading mode - updateWatchLink( $link, action ); - - // Format error message - var cleanTitle = title.replace( /_/g, ' ' ); - var link = mw.html.element( - 'a', { - 'href': mw.util.wikiGetlink( title ), - 'title': cleanTitle - }, cleanTitle - ); - var html = mw.msg( 'watcherrortext', link ); - - // Report to user about the error - mw.util.jsMessage( html, 'ajaxwatch' ); + api = new mw.Api(); + api[action]( + title, + // Success + function ( watchResponse ) { + var $li, otherAction; - } - ); - }); + otherAction = action === 'watch' ? 'unwatch' : 'watch'; + $li = $link.closest( 'li' ); -}); + mw.util.jsMessage( watchResponse.message, 'ajaxwatch' ); + + // Set link to opposite + updateWatchLink( $link, otherAction ); + + // Most common ID style + if ( $li.prop( 'id' ) === 'ca-' + otherAction || $li.prop( 'id' ) === 'ca-' + action ) { + $li.prop( 'id', 'ca-' + otherAction ); + } + + // Bug 12395 - update the watch checkbox on edit pages when the + // page is watched or unwatched via the tab. + if ( watchResponse.watched !== undefined ) { + $( '#wpWatchthis' ).prop( 'checked', true ); + } else { + $( '#wpWatchthis' ).removeProp( 'checked' ); + } + }, + // Error + function () { + var cleanTitle, html, link; + + // Reset link to non-loading mode + updateWatchLink( $link, action ); + + // Format error message + cleanTitle = title.replace( /_/g, ' ' ); + link = mw.html.element( + 'a', { + href: mw.util.wikiGetlink( title ), + title: cleanTitle + }, cleanTitle + ); + html = mw.msg( 'watcherrortext', link ); + + // Report to user about the error + mw.util.jsMessage( html, 'ajaxwatch' ); + + } + ); + }); + }); -})( jQuery, mediaWiki ); +}( jQuery, mediaWiki ) ); diff --git a/resources/mediawiki.api/mediawiki.api.watch.js b/resources/mediawiki.api/mediawiki.api.watch.js index 8ed6832091..302a2d31dc 100644 --- a/resources/mediawiki.api/mediawiki.api.watch.js +++ b/resources/mediawiki.api/mediawiki.api.watch.js @@ -4,47 +4,51 @@ */ ( function( $, mw ) { + /** + * @context {mw.Api} + */ + function doWatchInternal( page, success, err, addParams ) { + var params = { + action: 'watch', + title: String( page ), + token: mw.user.tokens.get( 'watchToken' ), + uselang: mw.config.get( 'wgUserLanguage' ) + }; + function ok( data ) { + success( data.watch ); + } + if ( addParams ) { + $.extend( params, addParams ); + } + return this.post( params, { ok: ok, err: err } ); + } + $.extend( mw.Api.prototype, { /** * Convinience method for 'action=watch'. * * @param page {String|mw.Title} Full page name or instance of mw.Title - * @param success {Function} callback to which the watch object will be passed - * watch object contains 'title' (full page name), 'watched' (boolean) and + * @param success {Function} Callback to which the watch object will be passed. + * Watch object contains properties 'title' (full pagename), 'watched' (boolean) and * 'message' (parsed HTML of the 'addedwatchtext' message). - * @param _unwatch {Boolean} Internally used to re-use this logic for unwatch(), - * do not use outside this module. - * @param err {Function} callback if error (optional) + * @param err {Function} Error callback (optional) * @return {jqXHR} */ - watch: function( page, success, err, _unwatch ) { - var params, ok; - params = { - action: 'watch', - title: String( page ), - token: mw.user.tokens.get( 'watchToken' ), - uselang: mw.config.get( 'wgUserLanguage' ) - }; - if ( _unwatch ) { - params.unwatch = 1; - } - ok = function( data ) { - success( data.watch ); - }; - return this.post( params, { ok: ok, err: err } ); + watch: function ( page, success, err ) { + return doWatchInternal.call( this, page, success, err ); }, /** * Convinience method for 'action=watch&unwatch=1'. * * @param page {String|mw.Title} Full page name or instance of mw.Title - * @param success {Function} callback to which the watch object will be passed - * watch object contains 'title' (full page name), 'unwatched' (boolean) and + * @param success {Function} Callback to which the watch object will be passed. + * Watch object contains properties 'title' (full pagename), 'watched' (boolean) and * 'message' (parsed HTML of the 'removedwatchtext' message). - * @param err {Function} callback if error (optional) + * @param err {Function} Error callback (optional) * @return {jqXHR} */ - unwatch: function( page, success, err ) { - return this.watch( page, success, err, true ); + unwatch: function ( page, success, err ) { + return doWatchInternal.call( this, page, success, err, { unwatch: 1 } ); } } ); diff --git a/skins/Vector.php b/skins/Vector.php index 501a267118..d1b51a688e 100644 --- a/skins/Vector.php +++ b/skins/Vector.php @@ -72,7 +72,7 @@ class VectorTemplate extends BaseTemplate { $nav = $this->data['content_navigation']; if ( $wgVectorUseIconWatch ) { - $mode = $this->getSkin()->getTitle()->userIsWatching() ? 'unwatch' : 'watch'; + $mode = $this->getSkin()->getRelevantTitle()->userIsWatching() ? 'unwatch' : 'watch'; if ( isset( $nav['actions'][$mode] ) ) { $nav['views'][$mode] = $nav['actions'][$mode]; $nav['views'][$mode]['class'] = rtrim( 'icon ' . $nav['views'][$mode]['class'], ' ' ); diff --git a/tests/phpunit/includes/RecentChangeTest.php b/tests/phpunit/includes/RecentChangeTest.php index 7865ed24b6..fbf271cc5c 100644 --- a/tests/phpunit/includes/RecentChangeTest.php +++ b/tests/phpunit/includes/RecentChangeTest.php @@ -1,4 +1,7 @@