Use PHP DateInputWidget in Contribs and use for range filtering

* Add two DateInputWidgets to Special:Contributions, one for start and
  one for end
** If start input is empty but end input is not, display edits up to end
   input, and vice versa
** If both inputs are specified, display edits between the two dates
** If both inputs are empty, no date range is used
* Legacy options (year=/month=) are converted to use for the end
  timestamp, so URLs with them should still work.
* Unit tests!

Bug: T120733
Change-Id: Id15f2b2ce2954fe98dfbbb7b0e86c0e4e5713f5e
This commit is contained in:
Geoffrey Mon 2016-12-12 09:26:15 -05:00 committed by Bartosz Dziewoński
parent 4b110c881c
commit b66888733c
5 changed files with 160 additions and 22 deletions

View file

@ -40,8 +40,11 @@ class SpecialContributions extends IncludableSpecialPage {
$out->addModuleStyles( [
'mediawiki.special',
'mediawiki.special.changeslist',
'mediawiki.widgets.DateInputWidget',
] );
$out->addModules( 'mediawiki.special.contributions' );
$this->addHelpLink( 'Help:User contributions' );
$out->enableOOUI();
$this->opts = [];
$request = $this->getRequest();
@ -130,8 +133,12 @@ class SpecialContributions extends IncludableSpecialPage {
$this->opts['year'] = '';
$this->opts['month'] = '';
} else {
$this->opts['year'] = $request->getIntOrNull( 'year' );
$this->opts['month'] = $request->getIntOrNull( 'month' );
$this->opts['year'] = $request->getVal( 'year' );
$this->opts['month'] = $request->getVal( 'month' );
$this->opts['start'] = $request->getVal( 'start' );
$this->opts['end'] = $request->getVal( 'end' );
$this->opts = SpecialContributions::processDateFilter( $this->opts );
}
$feedType = $request->getVal( 'feed' );
@ -190,8 +197,8 @@ class SpecialContributions extends IncludableSpecialPage {
'contribs' => $this->opts['contribs'],
'namespace' => $this->opts['namespace'],
'tagfilter' => $this->opts['tagfilter'],
'year' => $this->opts['year'],
'month' => $this->opts['month'],
'start' => $this->opts['start'],
'end' => $this->opts['end'],
'deletedOnly' => $this->opts['deletedOnly'],
'topOnly' => $this->opts['topOnly'],
'newOnly' => $this->opts['newOnly'],
@ -432,12 +439,12 @@ class SpecialContributions extends IncludableSpecialPage {
$this->opts['contribs'] = 'user';
}
if ( !isset( $this->opts['year'] ) ) {
$this->opts['year'] = '';
if ( !isset( $this->opts['start'] ) ) {
$this->opts['start'] = '';
}
if ( !isset( $this->opts['month'] ) ) {
$this->opts['month'] = '';
if ( !isset( $this->opts['end'] ) ) {
$this->opts['end'] = '';
}
if ( $this->opts['contribs'] == 'newbie' ) {
@ -478,6 +485,8 @@ class SpecialContributions extends IncludableSpecialPage {
'contribs',
'year',
'month',
'start',
'end',
'topOnly',
'newOnly',
'hideMinor',
@ -652,15 +661,32 @@ class SpecialContributions extends IncludableSpecialPage {
implode( '', $filters )
);
$dateSelectionAndSubmit = Xml::tags( 'div', [],
Xml::dateMenu(
$this->opts['year'] === '' ? MWTimestamp::getInstance()->format( 'Y' ) : $this->opts['year'],
$this->opts['month']
) . ' ' .
Html::submitButton(
$this->msg( 'sp-contributions-submit' )->text(),
[ 'class' => 'mw-submit' ], [ 'mw-ui-progressive' ]
)
$dateRangeSelection = Html::rawElement(
'div',
[],
Xml::label( wfMessage( 'date-range-from' )->text(), 'mw-date-start' ) . ' ' .
new \Mediawiki\Widget\DateInputWidget( [
'infusable' => true,
'id' => 'mw-date-start',
'name' => 'start',
'value' => $this->opts['start'],
'longDisplayFormat' => true,
] ) . '<br>' .
Xml::label( wfMessage( 'date-range-to' )->text(), 'mw-date-end' ) . ' ' .
new \Mediawiki\Widget\DateInputWidget( [
'infusable' => true,
'id' => 'mw-date-end',
'name' => 'end',
'value' => $this->opts['end'],
'longDisplayFormat' => true,
] )
);
$submit = Xml::tags( 'div', [],
Html::submitButton(
$this->msg( 'sp-contributions-submit' )->text(),
[ 'class' => 'mw-submit' ], [ 'mw-ui-progressive' ]
)
);
$form .= Xml::fieldset(
@ -669,7 +695,8 @@ class SpecialContributions extends IncludableSpecialPage {
$namespaceSelection .
$filterSelection .
$extraOptions .
$dateSelectionAndSubmit,
$dateRangeSelection .
$submit,
[ 'class' => 'mw-contributions-table' ]
);
@ -701,6 +728,46 @@ class SpecialContributions extends IncludableSpecialPage {
return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
}
/**
* Set up date filter options, given request data.
*
* @param array $opts Options array
* @return array Options array with processed start and end date filter options
*/
public static function processDateFilter( $opts ) {
$start = $opts['start'] ?: '';
$end = $opts['end'] ?: '';
$year = $opts['year'] ?: '';
$month = $opts['month'] ?: '';
if ( $start !== '' && $end !== '' &&
$start > $end
) {
$temp = $start;
$start = $end;
$end = $temp;
}
// If year/month legacy filtering options are set, convert them to display the new stamp
if ( $year !== '' || $month !== '' ) {
// Reuse getDateCond logic, but subtract a day because
// the endpoints of our date range appear inclusive
// but the internal end offsets are always exclusive
$legacyTimestamp = ReverseChronologicalPager::getOffsetDate( $year, $month );
$legacyDateTime = new DateTime( $legacyTimestamp->getTimestamp( TS_ISO_8601 ) );
$legacyDateTime = $legacyDateTime->modify( '-1 day' );
// Clear the new timestamp range options if used and
// replace with the converted legacy timestamp
$start = '';
$end = $legacyDateTime->format( 'Y-m-d' );
}
$opts['start'] = $start;
$opts['end'] = $end;
return $opts;
}
protected function getGroupName() {
return 'users';
}

View file

@ -28,7 +28,7 @@ use Wikimedia\Rdbms\ResultWrapper;
use Wikimedia\Rdbms\FakeResultWrapper;
use Wikimedia\Rdbms\IDatabase;
class ContribsPager extends ReverseChronologicalPager {
class ContribsPager extends RangeChronologicalPager {
public $mDefaultDirection = IndexPager::DIR_DESCENDING;
public $messages;
@ -76,9 +76,16 @@ class ContribsPager extends ReverseChronologicalPager {
$this->newOnly = !empty( $options['newOnly'] );
$this->hideMinor = !empty( $options['hideMinor'] );
$year = isset( $options['year'] ) ? $options['year'] : false;
$month = isset( $options['month'] ) ? $options['month'] : false;
$this->getDateCond( $year, $month );
// Date filtering: use timestamp if available
$startTimestamp = '';
$endTimestamp = '';
if ( $options['start'] ) {
$startTimestamp = $options['start'] . ' 00:00:00';
}
if ( $options['end'] ) {
$endTimestamp = $options['end'] . ' 23:59:59';
}
$this->getDateRangeCond( $startTimestamp, $endTimestamp );
// Most of this code will use the 'contributions' group DB, which can map to replica DBs
// with extra user based indexes or partioning by user. The additional metadata

View file

@ -1958,6 +1958,13 @@ return [
'mediawiki.special.comparepages.styles' => [
'styles' => 'resources/src/mediawiki.special/mediawiki.special.comparepages.styles.less',
],
'mediawiki.special.contributions' => [
'scripts' => 'resources/src/mediawiki.special/mediawiki.special.contributions.js',
'dependencies' => [
'mediawiki.widgets.DateInputWidget',
'mediawiki.jqueryMsg',
]
],
'mediawiki.special.edittags' => [
'scripts' => 'resources/src/mediawiki.special/mediawiki.special.edittags.js',
'dependencies' => [

View file

@ -0,0 +1,7 @@
/* jshint -W024*/
( function ( mw, $ ) {
$( function () {
mw.widgets.DateInputWidget.static.infuse( 'mw-date-start' );
mw.widgets.DateInputWidget.static.infuse( 'mw-date-end' );
} );
}( mediaWiki, jQuery ) );

View file

@ -0,0 +1,50 @@
<?php
/**
* @group Database
*/
class SpecialContributionsTest extends \PHPUnit_Framework_TestCase {
/**
* @dataProvider dateFilterOptionProcessingProvider
* @param array $inputOpts Input options
* @param array $expectedOpts Expected options
*/
public function testDateFilterOptionProcessing( $inputOpts, $expectedOpts ) {
$this->assertArraySubset( $expectedOpts, SpecialContributions::processDateFilter( $inputOpts ) );
}
public static function dateFilterOptionProcessingProvider() {
return [
[ [ 'start' => '2016-05-01',
'end' => '2016-06-01',
'year' => null,
'month' => null ],
[ 'start' => '2016-05-01',
'end' => '2016-06-01' ] ],
[ [ 'start' => '2016-05-01',
'end' => '2016-06-01',
'year' => '',
'month' => '' ],
[ 'start' => '2016-05-01',
'end' => '2016-06-01' ] ],
[ [ 'start' => '2016-05-01',
'end' => '2016-06-01',
'year' => '2012',
'month' => '5' ],
[ 'start' => '',
'end' => '2012-05-31' ] ],
[ [ 'start' => '',
'end' => '',
'year' => '2012',
'month' => '5' ],
[ 'start' => '',
'end' => '2012-05-31' ] ],
[ [ 'start' => '',
'end' => '',
'year' => '2012',
'month' => '' ],
[ 'start' => '',
'end' => '2012-12-31' ] ],
];
}
}