array( 'addPgField', 'recentchanges', 'rc_source', "TEXT NOT NULL DEFAULT ''" ),
array( 'addPgField', 'page', 'page_links_updated', "TIMESTAMPTZ NULL" ),
array( 'addPgField', 'mwuser', 'user_password_expires', 'TIMESTAMPTZ NULL' ),
+ array( 'changeFieldPurgeTable', 'l10n_cache', 'lc_value', 'bytea', "replace(lc_value,'\','\\\\')::bytea" ),
// 1.24
array( 'addPgField', 'page_props', 'pp_sortkey', 'float NULL' ),
}
}
+ protected function changeFieldPurgeTable( $table, $field, $newtype, $default ) {
+ ## For a cache table, empty it if the field needs to be changed, because the old contents
+ ## may be corrupted. If the column is already the desired type, refrain from purging.
+ $fi = $this->db->fieldInfo( $table, $field );
+ if ( is_null( $fi ) ) {
+ $this->output( "...ERROR: expected column $table.$field to exist\n" );
+ exit( 1 );
+ }
+
+ if ( $fi->type() === $newtype ) {
+ $this->output( "...column '$table.$field' is already of type '$newtype'\n" );
+ } else {
+ $this->output( "Purging data from cache table '$table'\n" );
+ $this->db->query("DELETE from $table" );
+ $this->output( "Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
+ $sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
+ if ( strlen( $default ) ) {
+ $res = array();
+ if ( preg_match( '/DEFAULT (.+)/', $default, $res ) ) {
+ $sqldef = "ALTER TABLE $table ALTER $field SET DEFAULT $res[1]";
+ $this->db->query( $sqldef );
+ $default = preg_replace( '/\s*DEFAULT .+/', '', $default );
+ }
+ $sql .= " USING $default";
+ }
+ $this->db->query( $sql );
+ }
+ }
+
protected function setDefault( $table, $field, $default ) {
$info = $this->db->fieldInfo( $table, $field );
/*!
- * OOjs UI v0.1.0-pre (9a6c625f5f)
+ * OOjs UI v0.1.0-pre (7d2507b267)
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2014 OOjs Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: Fri May 02 2014 12:04:40 GMT-0700 (PDT)
+ * Date: Mon May 05 2014 14:13:13 GMT-0700 (PDT)
*/
( function ( OO ) {
* @param {jQuery.Event} e Mouse down event
*/
OO.ui.ButtonedElement.prototype.onMouseDown = function ( e ) {
- if ( this.disabled || e.which !== 1 ) {
+ if ( this.isDisabled() || e.which !== 1 ) {
return false;
}
// tabIndex should generally be interacted with via the property,
* @param {jQuery.Event} e Mouse up event
*/
OO.ui.ButtonedElement.prototype.onMouseUp = function ( e ) {
- if ( this.disabled || e.which !== 1 ) {
+ if ( this.isDisabled() || e.which !== 1 ) {
return false;
}
// Restore the tab-index after the button is up to restore the button's accesssibility
* @param {jQuery.Event} e Mouse down event
*/
OO.ui.ToolGroup.prototype.onMouseDown = function ( e ) {
- if ( !this.disabled && e.which === 1 ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
this.pressed = this.getTargetTool( e );
if ( this.pressed ) {
this.pressed.setActive( true );
OO.ui.ToolGroup.prototype.onMouseUp = function ( e ) {
var tool = this.getTargetTool( e );
- if ( !this.disabled && e.which === 1 && this.pressed && this.pressed === tool ) {
+ if ( !this.isDisabled() && e.which === 1 && this.pressed && this.pressed === tool ) {
this.pressed.onSelect();
}
* @constructor
* @param {Object} [config] Configuration options
* @cfg {boolean} [continuous=false] Show all pages, one after another
- * @cfg {boolean} [autoFocus=false] Focus on the first focusable element when changing to a page
+ * @cfg {boolean} [autoFocus=true] Focus on the first focusable element when changing to a page
* @cfg {boolean} [outlined=false] Show an outline
* @cfg {boolean} [editable=false] Show controls for adding, removing and reordering pages
* @cfg {Object[]} [adders] List of adders for controls, each with name, icon and title properties
this.pages = {};
this.ignoreFocus = false;
this.stackLayout = new OO.ui.StackLayout( { '$': this.$, 'continuous': !!config.continuous } );
- this.autoFocus = !!config.autoFocus;
+ this.autoFocus = config.autoFocus === undefined ? true : !!config.autoFocus;
this.outlineVisible = false;
this.outlined = !!config.outlined;
if ( this.outlined ) {
* @inheritdoc
*/
OO.ui.PopupToolGroup.prototype.onMouseUp = function ( e ) {
- if ( !this.disabled && e.which === 1 ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
this.setActive( false );
}
return OO.ui.ToolGroup.prototype.onMouseUp.call( this, e );
* @param {jQuery.Event} e Mouse down event
*/
OO.ui.PopupToolGroup.prototype.onHandleMouseDown = function ( e ) {
- if ( !this.disabled && e.which === 1 ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
this.setActive( !this.active );
}
return false;
* @inheritdoc
*/
OO.ui.PopupTool.prototype.onSelect = function () {
- if ( !this.disabled ) {
+ if ( !this.isDisabled() ) {
if ( this.popup.isVisible() ) {
this.hidePopup();
} else {
* @fires click
*/
OO.ui.ButtonWidget.prototype.onClick = function () {
- if ( !this.disabled ) {
+ if ( !this.isDisabled() ) {
this.emit( 'click' );
if ( this.isHyperlink ) {
return true;
* @fires click
*/
OO.ui.ButtonWidget.prototype.onKeyPress = function ( e ) {
- if ( !this.disabled && e.which === OO.ui.Keys.SPACE ) {
+ if ( !this.isDisabled() && e.which === OO.ui.Keys.SPACE ) {
if ( this.isHyperlink ) {
this.onClick();
return true;
// Initialization
this.$input
.attr( 'name', config.name )
- .prop( 'disabled', this.disabled );
+ .prop( 'disabled', this.isDisabled() );
this.setReadOnly( config.readOnly );
this.$element.addClass( 'oo-ui-inputWidget' ).append( this.$input );
this.setValue( config.value );
* @param {jQuery.Event} e Key down, mouse up, cut, paste, change, input, or select event
*/
OO.ui.InputWidget.prototype.onEdit = function () {
- if ( !this.disabled ) {
+ if ( !this.isDisabled() ) {
// Allow the stack to clear so the value will be updated
setTimeout( OO.ui.bind( function () {
this.setValue( this.$input.val() );
OO.ui.InputWidget.prototype.setDisabled = function ( state ) {
OO.ui.Widget.prototype.setDisabled.call( this, state );
if ( this.$input ) {
- this.$input.prop( 'disabled', this.disabled );
+ this.$input.prop( 'disabled', this.isDisabled() );
}
return this;
};
* @inheritdoc
*/
OO.ui.CheckboxInputWidget.prototype.onEdit = function () {
- if ( !this.disabled ) {
+ if ( !this.isDisabled() ) {
// Allow the stack to clear so the value will be updated
setTimeout( OO.ui.bind( function () {
this.setValue( this.$input.prop( 'checked' ) );
* @return {boolean} Item is selectable
*/
OO.ui.OptionWidget.prototype.isSelectable = function () {
- return this.constructor.static.selectable && !this.disabled;
+ return this.constructor.static.selectable && !this.isDisabled();
};
/**
* @return {boolean} Item is highlightable
*/
OO.ui.OptionWidget.prototype.isHighlightable = function () {
- return this.constructor.static.highlightable && !this.disabled;
+ return this.constructor.static.highlightable && !this.isDisabled();
};
/**
* @return {boolean} Item is pressable
*/
OO.ui.OptionWidget.prototype.isPressable = function () {
- return this.constructor.static.pressable && !this.disabled;
+ return this.constructor.static.pressable && !this.isDisabled();
};
/**
* @chainable
*/
OO.ui.OptionWidget.prototype.setSelected = function ( state ) {
- if ( !this.disabled && this.constructor.static.selectable ) {
+ if ( !this.isDisabled() && this.constructor.static.selectable ) {
this.selected = !!state;
if ( this.selected ) {
this.$element.addClass( 'oo-ui-optionWidget-selected' );
* @chainable
*/
OO.ui.OptionWidget.prototype.setHighlighted = function ( state ) {
- if ( !this.disabled && this.constructor.static.highlightable ) {
+ if ( !this.isDisabled() && this.constructor.static.highlightable ) {
this.highlighted = !!state;
if ( this.highlighted ) {
this.$element.addClass( 'oo-ui-optionWidget-highlighted' );
* @chainable
*/
OO.ui.OptionWidget.prototype.setPressed = function ( state ) {
- if ( !this.disabled && this.constructor.static.pressable ) {
+ if ( !this.isDisabled() && this.constructor.static.pressable ) {
this.pressed = !!state;
if ( this.pressed ) {
this.$element.addClass( 'oo-ui-optionWidget-pressed' );
var $this = this.$element,
deferred = $.Deferred();
- if ( !this.disabled && this.constructor.static.pressable ) {
+ if ( !this.isDisabled() && this.constructor.static.pressable ) {
$this.removeClass( 'oo-ui-optionWidget-highlighted oo-ui-optionWidget-pressed' );
setTimeout( OO.ui.bind( function () {
// Restore original classes
OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
var item;
- if ( !this.disabled && e.which === 1 ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
this.togglePressed( true );
item = this.getTargetItem( e );
if ( item && item.isSelectable() ) {
this.selecting = item;
}
}
- if ( !this.disabled && e.which === 1 && this.selecting ) {
+ if ( !this.isDisabled() && e.which === 1 && this.selecting ) {
this.pressItem( null );
this.chooseItem( this.selecting );
this.selecting = null;
OO.ui.SelectWidget.prototype.onMouseMove = function ( e ) {
var item;
- if ( !this.disabled && this.pressed ) {
+ if ( !this.isDisabled() && this.pressed ) {
item = this.getTargetItem( e );
if ( item && item !== this.selecting && item.isSelectable() ) {
this.pressItem( item );
OO.ui.SelectWidget.prototype.onMouseOver = function ( e ) {
var item;
- if ( !this.disabled ) {
+ if ( !this.isDisabled() ) {
item = this.getTargetItem( e );
this.highlightItem( item && item.isHighlightable() ? item : null );
}
* @param {jQuery.Event} e Mouse over event
*/
OO.ui.SelectWidget.prototype.onMouseLeave = function () {
- if ( !this.disabled ) {
+ if ( !this.isDisabled() ) {
this.highlightItem( null );
}
return false;
handled = false,
highlightItem = this.getHighlightedItem();
- if ( !this.disabled && this.visible ) {
+ if ( !this.isDisabled() && this.visible ) {
if ( !highlightItem ) {
highlightItem = this.getSelectedItem();
}
return;
}
- if ( !this.disabled ) {
+ if ( !this.isDisabled() ) {
if ( this.menu.isVisible() ) {
this.menu.hide();
} else {
return;
}
- if ( !this.disabled ) {
+ if ( !this.isDisabled() ) {
if ( this.popup.isVisible() ) {
this.hidePopup();
} else {
* @inheritdoc
*/
OO.ui.ToggleButtonWidget.prototype.onClick = function () {
- if ( !this.disabled ) {
+ if ( !this.isDisabled() ) {
this.setValue( !this.value );
}
* @param {jQuery.Event} e Mouse down event
*/
OO.ui.ToggleSwitchWidget.prototype.onClick = function ( e ) {
- if ( !this.disabled && e.which === 1 ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
this.setValue( !this.value );
}
};
} );
} );
+ QUnit.test( 'postWithToken()', function ( assert ) {
+ QUnit.expect( 1 );
+
+ var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } );
+
+ // - Requests token
+ // - Performs action=example
+ api.postWithToken( 'testsimpletoken', { action: 'example', key: 'foo' } )
+ .done( function ( data ) {
+ assert.deepEqual( data, { example: { foo: 'quux' } } );
+ } );
+
+ this.server.requests[0].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "tokens": { "testsimpletokentoken": "a-bad-token" } }'
+ );
+
+ this.server.requests[1].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "example": { "foo": "quux" } }'
+ );
+ } );
+
+ QUnit.test( 'postWithToken() - badtoken', function ( assert ) {
+ QUnit.expect( 1 );
+
+ var api = new mw.Api();
+
+ // - Request: token
+ // - Request: action=example -> badtoken error
+ // - Request: new token
+ // - Request: action=example
+ api.postWithToken( 'testbadtoken', { action: 'example', key: 'foo' } )
+ .done( function ( data ) {
+ assert.deepEqual( data, { example: { foo: 'quux' } } );
+ } );
+
+ this.server.requests[0].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "tokens": { "testbadtokentoken": "a-bad-token" } }'
+ );
+
+ this.server.requests[1].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "error": { "code": "badtoken" } }'
+ );
+
+ this.server.requests[2].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "tokens": { "testbadtokentoken": "a-good-token" } }'
+ );
+
+ this.server.requests[3].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "example": { "foo": "quux" } }'
+ );
+
+ } );
+
+ QUnit.test( 'postWithToken() - badtoken-cached', function ( assert ) {
+ QUnit.expect( 2 );
+
+ var api = new mw.Api();
+
+ // - Request: token
+ // - Request: action=example
+ api.postWithToken( 'testbadtokencache', { action: 'example', key: 'foo' } )
+ .done( function ( data ) {
+ assert.deepEqual( data, { example: { foo: 'quux' } } );
+ } );
+
+ // - Cache: Try previously cached token
+ // - Request: action=example -> badtoken error
+ // - Request: new token
+ // - Request: action=example
+ api.postWithToken( 'testbadtokencache', { action: 'example', key: 'bar' } )
+ .done( function ( data ) {
+ assert.deepEqual( data, { example: { bar: 'quux' } } );
+ } );
+
+ this.server.requests[0].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "tokens": { "testbadtokencachetoken": "a-good-token-once" } }'
+ );
+
+ this.server.requests[1].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "example": { "foo": "quux" } }'
+ );
+
+ this.server.requests[2].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "error": { "code": "badtoken" } }'
+ );
+
+ this.server.requests[3].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "tokens": { "testbadtokencachetoken": "a-good-new-token" } }'
+ );
+
+ this.server.requests[4].respond( 200, { 'Content-Type': 'application/json' },
+ '{ "example": { "bar": "quux" } }'
+ );
+
+ } );
+
}( mediaWiki ) );