See the file UPGRADE for more detailed upgrade instructions.
-For notes on 1.24.x and older releases, see HISTORY.
+For notes on 1.25.x and older releases, see HISTORY.
== Online documentation ==
* @return bool|ResultWrapper
*/
static function loadAndLazyInit() {
+ global $wgMiserMode;
+
wfDebug( __METHOD__ . ": reading site_stats from slave\n" );
$row = self::doLoad( wfGetDB( DB_SLAVE ) );
$row = self::doLoad( wfGetDB( DB_MASTER ) );
}
- if ( !self::isSane( $row ) ) {
+ if ( !$wgMiserMode && !self::isSane( $row ) ) {
// Normally the site_stats table is initialized at install time.
// Some manual construction scenarios may leave the table empty or
// broken, however, for instance when importing from a dump into a
* page. Ignored if null or !$val.
*/
public function setNewtalk( $val, $curRev = null ) {
+ global $wgMemc;
+
if ( wfReadOnly() ) {
return;
}
$field = 'user_id';
$id = $this->getId();
}
- global $wgMemc;
if ( $val ) {
$changed = $this->updateNewtalk( $field, $id, $curRev );
}
/**
- * Immediately touch the user data cache for this account.
- * Updates user_touched field, and removes account data from memcached
- * for reload on the next hit.
+ * Immediately touch the user data cache for this account
+ *
+ * Calls touch() and removes account data from memcached
*/
public function invalidateCache() {
- if ( wfReadOnly() ) {
- return;
- }
- $this->load();
- if ( $this->mId ) {
- $this->mTouched = $this->newTouchedTimestamp();
-
- $dbw = wfGetDB( DB_MASTER );
- $userid = $this->mId;
- $touched = $this->mTouched;
- $method = __METHOD__;
- $dbw->onTransactionIdle( function () use ( $dbw, $userid, $touched, $method ) {
- // Prevent contention slams by checking user_touched first
- $encTouched = $dbw->addQuotes( $dbw->timestamp( $touched ) );
- $needsPurge = $dbw->selectField( 'user', '1',
- array( 'user_id' => $userid, 'user_touched < ' . $encTouched ) );
- if ( $needsPurge ) {
- $dbw->update( 'user',
- array( 'user_touched' => $dbw->timestamp( $touched ) ),
- array( 'user_id' => $userid, 'user_touched < ' . $encTouched ),
- $method
- );
- }
- } );
- $this->clearSharedCache();
- }
+ $this->touch();
+ $this->clearSharedCache();
}
/**
* @param {Function} options.result.select Called in context of the suggestions-result-current element.
* @param {jQuery} options.result.select.$textbox
*
+ * @param {Object} [options.update] Set of callbacks for listening to a change in the text input.
+ *
+ * @param {Function} options.update.before Called right after the user changes the textbox text.
+ * @param {Function} options.update.after Called after results are updated either from the cache or
+ * the API as a result of the user input.
+ *
* @param {jQuery} [options.$region=this] The element to place the suggestions below and match width of.
*
* @param {string[]} [options.suggestions] Array of suggestions to display.
* @param {boolean} [options.positionFromLeft] Sets `expandFrom=left`, for backwards
* compatibility.
*
- * @param {boolean} [options.highlightInput=false] Whether to hightlight matched portions of the
+ * @param {boolean} [options.highlightInput=false] Whether to highlight matched portions of the
* input or not.
*/
( function ( $ ) {
cache = context.data.cache,
cacheHit;
+ if ( typeof context.config.update.before === 'function' ) {
+ context.config.update.before.call( context.data.$textbox );
+ }
+
// Only fetch if the value in the textbox changed and is not empty, or if the results were hidden
// if the textbox is empty then clear the result div, but leave other settings intouched
if ( val.length === 0 ) {
if ( context.config.cache && hasOwn.call( cache, val ) ) {
if ( +new Date() - cache[ val ].timestamp < context.config.cacheMaxAge ) {
context.data.$textbox.suggestions( 'suggestions', cache[ val ].suggestions );
+ if ( typeof context.config.update.after === 'function' ) {
+ context.config.update.after.call( context.data.$textbox );
+ }
cacheHit = true;
} else {
// Cache expired
function ( suggestions ) {
suggestions = suggestions.slice( 0, context.config.maxRows );
context.data.$textbox.suggestions( 'suggestions', suggestions );
+ if ( typeof context.config.update.after === 'function' ) {
+ context.config.update.after.call( context.data.$textbox );
+ }
if ( context.config.cache ) {
cache[ val ] = {
suggestions: suggestions,
case 'cancel':
case 'special':
case 'result':
+ case 'update':
case '$region':
case 'expandFrom':
context.config[property] = value;
cancel: function () {},
special: {},
result: {},
+ update: {},
$region: $( this ),
suggestions: [],
maxRows: 10,
// element (not the search form, as that would leave the buttons
// vertically between the input and the suggestions).
$searchRegion = $( '#simpleSearch, #searchInput' ).first(),
- $searchInput = $( '#searchInput' );
+ $searchInput = $( '#searchInput' ),
+ previousSearchText = $searchInput.val();
// Compatibility map
map = {
};
}
+ /**
+ * Callback that's run when the user changes the search input text
+ * 'this' is the search input box (jQuery object)
+ * @ignore
+ */
+ function onBeforeUpdate() {
+ var searchText = this.val();
+
+ if ( searchText && searchText !== previousSearchText ) {
+ mw.track( 'mediawiki.searchSuggest', {
+ action: 'session-start'
+ } );
+ }
+ previousSearchText = searchText;
+ }
+
+ /**
+ * Callback that's run when suggestions have been updated either from the cache or the API
+ * 'this' is the search input box (jQuery object)
+ * @ignore
+ */
+ function onAfterUpdate() {
+ var context = this.data( 'suggestionsContext' );
+
+ mw.track( 'mediawiki.searchSuggest', {
+ action: 'impression-results',
+ numberOfResults: context.config.suggestions.length,
+ // FIXME: when other types of search become available change this value accordingly
+ // See the API call below (opensearch = prefix)
+ resultSetType: 'prefix'
+ } );
+ }
+
// The function used to render the suggestions.
function renderFunction( text, context ) {
if ( !resultRenderCache ) {
);
}
+ // The function used when the user makes a selection
+ function selectFunction( $input ) {
+ var context = $input.data( 'suggestionsContext' ),
+ text = $input.val();
+
+ mw.track( 'mediawiki.searchSuggest', {
+ action: 'click-result',
+ numberOfResults: context.config.suggestions.length,
+ clickIndex: context.config.suggestions.indexOf( text ) + 1
+ } );
+
+ // allow the form to be submitted
+ return true;
+ }
+
function specialRenderFunction( query, context ) {
var $el = this;
return;
}
- // Special suggestions functionality for skin-provided search box
+ // Special suggestions functionality and tracking for skin-provided search box
$searchInput.suggestions( {
+ update: {
+ before: onBeforeUpdate,
+ after: onAfterUpdate
+ },
+ result: {
+ render: renderFunction,
+ select: selectFunction
+ },
special: {
render: specialRenderFunction,
select: function ( $input ) {
* - protect/protect
* - protect/modifyprotect
* - protect/unprotect
+ * - protect/move_prot
* - upload/upload
* - merge/merge
* - import/upload
$protectParams,
$this->user_comment
);
+
+ # protect/move_prot
+ $this->assertIRCComment(
+ $this->context->msg( 'movedarticleprotection', 'SomeTitle', 'OldTitle' )
+ ->plain() . $sep . $this->user_comment,
+ 'protect', 'move_prot',
+ array( 'OldTitle' ),
+ $this->user_comment
+ );
}
/**