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
66 public function __construct(
67 IContextSource $context,
68 array $options,
69 ?LinkRenderer $linkRenderer = null,
70 ?LinkBatchFactory $linkBatchFactory = null,
71 ?HookContainer $hookContainer = null,
72 ?IConnectionProvider $dbProvider = null,
76 ?CommentFormatter $commentFormatter = null
77 ) {
78 // Class is used directly in extensions - T266484
80 $dbProvider ??= $services->getConnectionProvider();
81
82 parent::__construct(
83 $linkRenderer ?? $services->getLinkRenderer(),
84 $linkBatchFactory ?? $services->getLinkBatchFactory(),
85 $hookContainer ?? $services->getHookContainer(),
86 $revisionStore ?? $services->getRevisionStore(),
87 $namespaceInfo ?? $services->getNamespaceInfo(),
88 $commentFormatter ?? $services->getCommentFormatter(),
89 $services->getUserFactory(),
90 $context,
91 $options,
93 );
94 }
95
105 private function getTargetTable() {
106 $dbr = $this->getDatabase();
107 $ipRangeConds = $this->targetUser->isRegistered()
108 ? null : $this->getIpRangeConds( $dbr, $this->target );
109 if ( $ipRangeConds ) {
110 return 'ip_changes';
111 }
112
113 return 'revision';
114 }
115
116 protected function getRevisionQuery() {
117 $revQuery = $this->revisionStore->getQueryInfo( [ 'page', 'user' ] );
118 $queryInfo = [
119 'tables' => $revQuery['tables'],
120 'fields' => array_merge( $revQuery['fields'], [ 'page_is_new' ] ),
121 'conds' => [],
122 'options' => [],
123 'join_conds' => $revQuery['joins'],
124 ];
125
126 // WARNING: Keep this in sync with getTargetTable()!
127 $ipRangeConds = !$this->targetUser->isRegistered() ?
128 $this->getIpRangeConds( $this->getDatabase(), $this->target ) :
129 null;
130 if ( $ipRangeConds ) {
131 // Put ip_changes first (T284419)
132 array_unshift( $queryInfo['tables'], 'ip_changes' );
133 $queryInfo['join_conds']['revision'] = [
134 'JOIN', [ 'rev_id = ipc_rev_id' ]
135 ];
136 $queryInfo['conds'][] = $ipRangeConds;
137 } else {
138 $queryInfo['conds']['actor_name'] = $this->targetUser->getName();
139 // Force the appropriate index to avoid bad query plans (T307295)
140 $queryInfo['options']['USE INDEX']['revision'] = 'rev_actor_timestamp';
141 }
142
143 return $queryInfo;
144 }
145
152 private function getIpRangeConds( $db, $ip ) {
153 // First make sure it is a valid range and they are not outside the CIDR limit
154 if ( !$this->isQueryableRange( $ip, $this->getConfig() ) ) {
155 return false;
156 }
157
158 [ $start, $end ] = IPUtils::parseRange( $ip );
159
160 return $db->expr( 'ipc_hex', '>=', $start )->and( 'ipc_hex', '<=', $end );
161 }
162
166 public function getIndexField() {
167 // The returned column is used for sorting and continuation, so we need to
168 // make sure to use the right denormalized column depending on which table is
169 // being targeted by the query to avoid bad query plans.
170 // See T200259, T204669, T220991, and T221380.
171 $target = $this->getTargetTable();
172 switch ( $target ) {
173 case 'revision':
174 return 'rev_timestamp';
175 case 'ip_changes':
176 return 'ipc_rev_timestamp';
177 default:
178 wfWarn(
179 __METHOD__ . ": Unknown value '$target' from " . static::class . '::getTargetTable()', 0
180 );
181 return 'rev_timestamp';
182 }
183 }
184
188 protected function getExtraSortFields() {
189 // The returned columns are used for sorting, so we need to make sure
190 // to use the right denormalized column depending on which table is
191 // being targeted by the query to avoid bad query plans.
192 // See T200259, T204669, T220991, and T221380.
193 $target = $this->getTargetTable();
194 switch ( $target ) {
195 case 'revision':
196 return [ 'rev_id' ];
197 case 'ip_changes':
198 return [ 'ipc_rev_id' ];
199 default:
200 wfWarn(
201 __METHOD__ . ": Unknown value '$target' from " . static::class . '::getTargetTable()', 0
202 );
203 return [ 'rev_id' ];
204 }
205 }
206
213 public static function processDateFilter( array $opts ) {
214 $start = $opts['start'] ?? '';
215 $end = $opts['end'] ?? '';
216 $year = $opts['year'] ?? '';
217 $month = $opts['month'] ?? '';
218
219 if ( $start !== '' && $end !== '' && $start > $end ) {
220 $temp = $start;
221 $start = $end;
222 $end = $temp;
223 }
224
225 // If year/month legacy filtering options are set, convert them to display the new stamp
226 if ( $year !== '' || $month !== '' ) {
227 // Reuse getDateCond logic, but subtract a day because
228 // the endpoints of our date range appear inclusive
229 // but the internal end offsets are always exclusive
230 $legacyTimestamp = ReverseChronologicalPager::getOffsetDate( $year, $month );
231 $legacyDateTime = new DateTime( $legacyTimestamp->getTimestamp( TS_ISO_8601 ) );
232 $legacyDateTime = $legacyDateTime->modify( '-1 day' );
233
234 // Clear the new timestamp range options if used and
235 // replace with the converted legacy timestamp
236 $start = '';
237 $end = $legacyDateTime->format( 'Y-m-d' );
238 }
239
240 $opts['start'] = $start;
241 $opts['end'] = $end;
242
243 return $opts;
244 }
245}
246
251class_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.