* @cfg {string} savedQueriesPreferenceName Where to save the saved queries
* @cfg {string} daysPreferenceName Preference name for the days filter
* @cfg {string} limitPreferenceName Preference name for the limit filter
+ * @cfg {boolean} [normalizeTarget] Dictates whether or not to go through the
+ * title normalization to separate title subpage/parts into the target= url
+ * parameter
*/
mw.rcfilters.Controller = function MwRcfiltersController( filtersModel, changesListModel, savedQueriesModel, config ) {
this.filtersModel = filtersModel;
this.savedQueriesPreferenceName = config.savedQueriesPreferenceName;
this.daysPreferenceName = config.daysPreferenceName;
this.limitPreferenceName = config.limitPreferenceName;
+ this.normalizeTarget = !!config.normalizeTarget;
this.requestCounter = {};
this.baseFilterState = {};
* @param {Array} filterStructure Filter definition and structure for the model
* @param {Object} [namespaceStructure] Namespace definition
* @param {Object} [tagList] Tag definition
+ * @param {Object} [conditionalViews] Conditional view definition
*/
- mw.rcfilters.Controller.prototype.initialize = function ( filterStructure, namespaceStructure, tagList ) {
+ mw.rcfilters.Controller.prototype.initialize = function ( filterStructure, namespaceStructure, tagList, conditionalViews ) {
var parsedSavedQueries, pieces,
displayConfig = mw.config.get( 'StructuredChangeFiltersDisplayConfig' ),
defaultSavedQueryExists = mw.config.get( 'wgStructuredChangeFiltersDefaultSavedQueryExists' ),
controller = this,
- views = {},
+ views = $.extend( true, {}, conditionalViews ),
items = [],
uri = new mw.Uri();
separator: ';',
fullCoverage: true,
filters: items
- },
- {
- name: 'invertGroup',
- type: 'boolean',
- hidden: true,
- filters: [ {
- name: 'invert',
- 'default': '0'
- } ]
} ]
};
+ views.invert = {
+ groups: [
+ {
+ name: 'invertGroup',
+ type: 'boolean',
+ hidden: true,
+ filters: [ {
+ name: 'invert',
+ 'default': '0'
+ } ]
+ } ]
+ };
}
if ( tagList ) {
views.tags = {
this.filtersModel.initializeFilters( filterStructure, views );
this.uriProcessor = new mw.rcfilters.UriProcessor(
- this.filtersModel
+ this.filtersModel,
+ { normalizeTarget: this.normalizeTarget }
);
if ( !mw.user.isAnon() ) {
* Extracts information from the changes list DOM
*
* @param {jQuery} $root Root DOM to find children from
+ * @param {boolean} [statusCode] Server response status code
* @return {Object} Information about changes list
* @return {Object|string} return.changes Changes list, or 'NO_RESULTS' if there are no results
* (either normally or as an error)
* 'NO_RESULTS_TIMEOUT' for no results due to a timeout, or omitted for more than 0 results
* @return {jQuery} return.fieldset Fieldset
*/
- mw.rcfilters.Controller.prototype._extractChangesListInfo = function ( $root ) {
- var info, isTimeout,
+ mw.rcfilters.Controller.prototype._extractChangesListInfo = function ( $root, statusCode ) {
+ var info,
$changesListContents = $root.find( '.mw-changeslist' ).first().contents(),
- areResults = !!$changesListContents.length;
+ areResults = !!$changesListContents.length,
+ checkForLogout = !areResults && statusCode === 200;
+
+ // We check if user logged out on different tab/browser or the session has expired.
+ // 205 status code returned from the server, which indicates that we need to reload the page
+ // is not usable on WL page, because we get redirected to login page, which gives 200 OK
+ // status code (if everything else goes well).
+ // Bug: T177717
+ if ( checkForLogout && !!$root.find( '#wpName1' ).length ) {
+ location.reload( false );
+ return;
+ }
info = {
changes: $changesListContents.length ? $changesListContents : 'NO_RESULTS',
};
if ( !areResults ) {
- isTimeout = !!$root.find( '.mw-changeslist-timeout' ).length;
- info.noResultsDetails = isTimeout ? 'NO_RESULTS_TIMEOUT' : 'NO_RESULTS_NORMAL';
+ if ( $root.find( '.mw-changeslist-timeout' ).length ) {
+ info.noResultsDetails = 'NO_RESULTS_TIMEOUT';
+ } else if ( $root.find( '.mw-changeslist-notargetpage' ).length ) {
+ info.noResultsDetails = 'NO_RESULTS_NO_TARGET_PAGE';
+ } else {
+ info.noResultsDetails = 'NO_RESULTS_NORMAL';
+ }
}
return info;
} );
};
- /**
- * Switch the view of the filters model
- *
- * @param {string} view Requested view
- */
- mw.rcfilters.Controller.prototype.switchView = function ( view ) {
- this.filtersModel.switchView( view );
- };
-
/**
* Reset to default filters
*/
*/
mw.rcfilters.Controller.prototype.toggleInvertedNamespaces = function () {
this.filtersModel.toggleInvertedNamespaces();
-
if (
this.filtersModel.getFiltersByView( 'namespaces' ).filter(
function ( filterItem ) { return filterItem.isSelected(); }
) {
// Only re-fetch results if there are namespace items that are actually selected
this.updateChangesList();
+ } else {
+ this.uriProcessor.updateURL();
}
};
+ /**
+ * Set the value of the 'showlinkedto' parameter
+ * @param {boolean} value
+ */
+ mw.rcfilters.Controller.prototype.setShowLinkedTo = function ( value ) {
+ var targetItem = this.filtersModel.getGroup( 'page' ).getItemByParamName( 'target' ),
+ showLinkedToItem = this.filtersModel.getGroup( 'toOrFrom' ).getItemByParamName( 'showlinkedto' );
+
+ this.filtersModel.toggleFilterSelected( showLinkedToItem.getName(), value );
+ this.uriProcessor.updateURL();
+ // reload the results only when target is set
+ if ( targetItem.getValue() ) {
+ this.updateChangesList();
+ }
+ };
+
+ /**
+ * Set the target page
+ * @param {string} page
+ */
+ mw.rcfilters.Controller.prototype.setTargetPage = function ( page ) {
+ var targetItem = this.filtersModel.getGroup( 'page' ).getItemByParamName( 'target' );
+ targetItem.setValue( page );
+ this.uriProcessor.updateURL();
+ this.updateChangesList();
+ };
+
/**
* Set the highlight color for a filter item
*
}
this._checkForNewChanges()
- .then( function ( newChanges ) {
+ .then( function ( statusCode ) {
+ // no result is 204 with the 'peek' param
+ // logged out is 205
+ var newChanges = statusCode === 200;
+
if ( !this._shouldCheckForNewChanges() ) {
// by the time the response is received,
// it may not be appropriate anymore
return;
}
+ // 205 is the status code returned from server when user's logged in/out
+ // status is not matching while fetching live update changes.
+ // This works only on Recent Changes page. For WL, look _extractChangesListInfo.
+ // Bug: T177717
+ if ( statusCode === 205 ) {
+ location.reload( false );
+ return;
+ }
+
if ( newChanges ) {
if ( this.changesListModel.getLiveUpdate() ) {
return this.updateChangesList( null, this.LIVE_UPDATE );
var params = {
limit: 1,
peek: 1, // bypasses ChangesList specific UI
- from: this.changesListModel.getNextFrom()
+ from: this.changesListModel.getNextFrom(),
+ isAnon: mw.user.isAnon()
};
return this._queryChangesList( 'liveUpdate', params ).then(
function ( data ) {
- // no result is 204 with the 'peek' param
- return data.status === 200;
+ return data.status;
}
);
};
mw.rcfilters.Controller.prototype.updateStateFromUrl = function ( fetchChangesList ) {
fetchChangesList = fetchChangesList === undefined ? true : !!fetchChangesList;
- this.uriProcessor.updateModelBasedOnQuery( new mw.Uri().query );
+ this.uriProcessor.updateModelBasedOnQuery();
// Update the sticky preferences, in case we received a value
// from the URL
};
}
- $parsed = $( '<div>' ).append( $( $.parseHTML( data.content ) ) );
-
- return this._extractChangesListInfo( $parsed );
+ $parsed = $( '<div>' ).append( $( $.parseHTML(
+ data ? data.content : ''
+ ) ) );
+ return this._extractChangesListInfo( $parsed, data.status );
}.bind( this )
);
};
this.updateChangesList( null, 'markSeen' );
}.bind( this ) );
};
+
+ /**
+ * Set the current search for the system.
+ *
+ * @param {string} searchQuery Search query, including triggers
+ */
+ mw.rcfilters.Controller.prototype.setSearch = function ( searchQuery ) {
+ this.filtersModel.setSearch( searchQuery );
+ };
+
+ /**
+ * Switch the view by changing the search query trigger
+ * without changing the search term
+ *
+ * @param {string} view View to change to
+ */
+ mw.rcfilters.Controller.prototype.switchView = function ( view ) {
+ this.setSearch(
+ this.filtersModel.getViewTrigger( view ) +
+ this.filtersModel.removeViewTriggers( this.filtersModel.getSearch() )
+ );
+ };
+
+ /**
+ * Reset the search for a specific view. This means we null the search query
+ * and replace it with the relevant trigger for the requested view
+ *
+ * @param {string} [view='default'] View to change to
+ */
+ mw.rcfilters.Controller.prototype.resetSearchForView = function ( view ) {
+ view = view || 'default';
+
+ this.setSearch(
+ this.filtersModel.getViewTrigger( view )
+ );
+ };
}( mediaWiki, jQuery ) );