MediaWiki master
ContribsPager.php
Go to the documentation of this file.
1<?php
22namespace MediaWiki\Pager;
23
24use DateTime;
32use MediaWiki\SpecialPage\ContributionsRangeTrait;
35use Wikimedia\IPUtils;
39
50
51 use ContributionsRangeTrait;
52
56 public function __construct(
57 IContextSource $context,
58 array $options,
59 ?LinkRenderer $linkRenderer = null,
60 ?LinkBatchFactory $linkBatchFactory = null,
61 ?HookContainer $hookContainer = null,
62 ?IConnectionProvider $dbProvider = null,
66 ?CommentFormatter $commentFormatter = null
67 ) {
68 // Class is used directly in extensions - T266484
70 $dbProvider ??= $services->getConnectionProvider();
71
72 parent::__construct(
73 $linkRenderer ?? $services->getLinkRenderer(),
74 $linkBatchFactory ?? $services->getLinkBatchFactory(),
75 $hookContainer ?? $services->getHookContainer(),
76 $revisionStore ?? $services->getRevisionStore(),
77 $namespaceInfo ?? $services->getNamespaceInfo(),
78 $commentFormatter ?? $services->getCommentFormatter(),
79 $services->getUserFactory(),
80 $context,
81 $options,
83 );
84 }
85
95 private function getTargetTable() {
96 $dbr = $this->getDatabase();
97 $ipRangeConds = $this->targetUser->isRegistered()
98 ? null : $this->getIpRangeConds( $dbr, $this->target );
99 if ( $ipRangeConds ) {
100 return 'ip_changes';
101 }
102
103 return 'revision';
104 }
105
106 protected function getRevisionQuery() {
107 $revQuery = $this->revisionStore->getQueryInfo( [ 'page', 'user' ] );
108 $queryInfo = [
109 'tables' => $revQuery['tables'],
110 'fields' => array_merge( $revQuery['fields'], [ 'page_is_new' ] ),
111 'conds' => [],
112 'options' => [],
113 'join_conds' => $revQuery['joins'],
114 ];
115
116 // WARNING: Keep this in sync with getTargetTable()!
117 $ipRangeConds = !$this->targetUser->isRegistered() ?
118 $this->getIpRangeConds( $this->getDatabase(), $this->target ) :
119 null;
120 if ( $ipRangeConds ) {
121 // Put ip_changes first (T284419)
122 array_unshift( $queryInfo['tables'], 'ip_changes' );
123 $queryInfo['join_conds']['revision'] = [
124 'JOIN', [ 'rev_id = ipc_rev_id' ]
125 ];
126 $queryInfo['conds'][] = $ipRangeConds;
127 } else {
128 $queryInfo['conds']['actor_name'] = $this->targetUser->getName();
129 // Force the appropriate index to avoid bad query plans (T307295)
130 $queryInfo['options']['USE INDEX']['revision'] = 'rev_actor_timestamp';
131 }
132
133 return $queryInfo;
134 }
135
142 private function getIpRangeConds( $db, $ip ) {
143 // First make sure it is a valid range and they are not outside the CIDR limit
144 if ( !$this->isQueryableRange( $ip, $this->getConfig() ) ) {
145 return false;
146 }
147
148 [ $start, $end ] = IPUtils::parseRange( $ip );
149
150 return $db->expr( 'ipc_hex', '>=', $start )->and( 'ipc_hex', '<=', $end );
151 }
152
156 public function getIndexField() {
157 // The returned column is used for sorting and continuation, so we need to
158 // make sure to use the right denormalized column depending on which table is
159 // being targeted by the query to avoid bad query plans.
160 // See T200259, T204669, T220991, and T221380.
161 $target = $this->getTargetTable();
162 switch ( $target ) {
163 case 'revision':
164 return 'rev_timestamp';
165 case 'ip_changes':
166 return 'ipc_rev_timestamp';
167 default:
168 wfWarn(
169 __METHOD__ . ": Unknown value '$target' from " . static::class . '::getTargetTable()', 0
170 );
171 return 'rev_timestamp';
172 }
173 }
174
178 protected function getExtraSortFields() {
179 // The returned columns are used for sorting, so we need to make sure
180 // to use the right denormalized column depending on which table is
181 // being targeted by the query to avoid bad query plans.
182 // See T200259, T204669, T220991, and T221380.
183 $target = $this->getTargetTable();
184 switch ( $target ) {
185 case 'revision':
186 return [ 'rev_id' ];
187 case 'ip_changes':
188 return [ 'ipc_rev_id' ];
189 default:
190 wfWarn(
191 __METHOD__ . ": Unknown value '$target' from " . static::class . '::getTargetTable()', 0
192 );
193 return [ 'rev_id' ];
194 }
195 }
196
203 public static function processDateFilter( array $opts ) {
204 $start = $opts['start'] ?? '';
205 $end = $opts['end'] ?? '';
206 $year = $opts['year'] ?? '';
207 $month = $opts['month'] ?? '';
208
209 if ( $start !== '' && $end !== '' && $start > $end ) {
210 $temp = $start;
211 $start = $end;
212 $end = $temp;
213 }
214
215 // If year/month legacy filtering options are set, convert them to display the new stamp
216 if ( $year !== '' || $month !== '' ) {
217 // Reuse getDateCond logic, but subtract a day because
218 // the endpoints of our date range appear inclusive
219 // but the internal end offsets are always exclusive
220 $legacyTimestamp = ReverseChronologicalPager::getOffsetDate( $year, $month );
221 $legacyDateTime = new DateTime( $legacyTimestamp->getTimestamp( TS_ISO_8601 ) );
222 $legacyDateTime = $legacyDateTime->modify( '-1 day' );
223
224 // Clear the new timestamp range options if used and
225 // replace with the converted legacy timestamp
226 $start = '';
227 $end = $legacyDateTime->format( 'Y-m-d' );
228 }
229
230 $opts['start'] = $start;
231 $opts['end'] = $end;
232
233 return $opts;
234 }
235}
236
241class_alias( ContribsPager::class, 'ContribsPager' );
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
This is the main service interface for converting single-line comments from various DB comment fields...
Class that generates HTML for internal links.
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Pager for Special:Contributions.
__construct(IContextSource $context, array $options, ?LinkRenderer $linkRenderer=null, ?LinkBatchFactory $linkBatchFactory=null, ?HookContainer $hookContainer=null, ?IConnectionProvider $dbProvider=null, ?RevisionStore $revisionStore=null, ?NamespaceInfo $namespaceInfo=null, ?UserIdentity $targetUser=null, ?CommentFormatter $commentFormatter=null)
FIXME List services first T266484 / T290405.
getRevisionQuery()
Get queryInfo for the main query selecting revisions, not including filtering on namespace,...
static processDateFilter(array $opts)
Set up date filter options, given request data.
Pager for Special:Contributions.
string $target
User name, or a string describing an IP address range.
getDatabase()
Get the Database object in use.
static getOffsetDate( $year, $month, $day=-1)
Core logic of determining the offset timestamp such that we can get all items with a timestamp up to ...
Service for looking up page revisions.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Interface for objects which can provide a MediaWiki context on request.
Interface for objects representing user identity.
Provide primary and replica IDatabase connections.
A database connection without write operations.