MediaWiki master
ContribsPager.php
Go to the documentation of this file.
1<?php
9
10use DateTime;
11use MediaWiki\Cache\LinkBatchFactory;
20use MediaWiki\SpecialPage\ContributionsRangeTrait;
23use Wikimedia\IPUtils;
27use Wikimedia\Timestamp\TimestampFormat as TS;
28
39
40 use ContributionsRangeTrait;
41
45 public function __construct(
46 IContextSource $context,
47 array $options,
48 ?LinkRenderer $linkRenderer = null,
49 ?LinkBatchFactory $linkBatchFactory = null,
50 ?HookContainer $hookContainer = null,
51 ?IConnectionProvider $dbProvider = null,
55 ?CommentFormatter $commentFormatter = null
56 ) {
57 // Class is used directly in extensions - T266484
59 $dbProvider ??= $services->getConnectionProvider();
60
61 parent::__construct(
62 $linkRenderer ?? $services->getLinkRenderer(),
63 $linkBatchFactory ?? $services->getLinkBatchFactory(),
64 $hookContainer ?? $services->getHookContainer(),
65 $revisionStore ?? $services->getRevisionStore(),
66 $namespaceInfo ?? $services->getNamespaceInfo(),
67 $commentFormatter ?? $services->getCommentFormatter(),
68 $services->getUserFactory(),
69 $context,
70 $options,
72 );
73 }
74
84 private function getTargetTable() {
85 $dbr = $this->getDatabase();
86 $ipRangeConds = $this->targetUser->isRegistered()
87 ? null : $this->getIpRangeConds( $dbr, $this->target );
88 if ( $ipRangeConds ) {
89 return 'ip_changes';
90 }
91
92 return 'revision';
93 }
94
96 protected function getRevisionQuery() {
97 $revQuery = $this->revisionStore->getQueryInfo( [ 'page', 'user' ] );
98 $queryInfo = [
99 'tables' => $revQuery['tables'],
100 'fields' => array_merge( $revQuery['fields'], [ 'page_is_new' ] ),
101 'conds' => [],
102 'options' => [],
103 'join_conds' => $revQuery['joins'],
104 ];
105
106 // WARNING: Keep this in sync with getTargetTable()!
107 $ipRangeConds = !$this->targetUser->isRegistered() ?
108 $this->getIpRangeConds( $this->getDatabase(), $this->target ) :
109 null;
110 if ( $ipRangeConds ) {
111 // Put ip_changes first (T284419)
112 array_unshift( $queryInfo['tables'], 'ip_changes' );
113 $queryInfo['join_conds']['revision'] = [
114 'JOIN', [ 'rev_id = ipc_rev_id' ]
115 ];
116 $queryInfo['conds'][] = $ipRangeConds;
117 } else {
118 $queryInfo['conds']['actor_name'] = $this->targetUser->getName();
119 // Force the appropriate index to avoid bad query plans (T307295)
120 $queryInfo['options']['USE INDEX']['revision'] = 'rev_actor_timestamp';
121 }
122
123 return $queryInfo;
124 }
125
132 private function getIpRangeConds( $db, $ip ) {
133 // First make sure it is a valid range and they are not outside the CIDR limit
134 if ( !$this->isQueryableRange( $ip, $this->getConfig() ) ) {
135 return false;
136 }
137
138 [ $start, $end ] = IPUtils::parseRange( $ip );
139
140 return $db->expr( 'ipc_hex', '>=', $start )->and( 'ipc_hex', '<=', $end );
141 }
142
146 public function getIndexField() {
147 // The returned column is used for sorting and continuation, so we need to
148 // make sure to use the right denormalized column depending on which table is
149 // being targeted by the query to avoid bad query plans.
150 // See T200259, T204669, T220991, and T221380.
151 $target = $this->getTargetTable();
152 switch ( $target ) {
153 case 'revision':
154 return 'rev_timestamp';
155 case 'ip_changes':
156 return 'ipc_rev_timestamp';
157 default:
158 wfWarn(
159 __METHOD__ . ": Unknown value '$target' from " . static::class . '::getTargetTable()', 0
160 );
161 return 'rev_timestamp';
162 }
163 }
164
168 protected function getExtraSortFields() {
169 // The returned columns are used for sorting, so we need to make sure
170 // to use the right denormalized column depending on which table is
171 // being targeted by the query to avoid bad query plans.
172 // See T200259, T204669, T220991, and T221380.
173 $target = $this->getTargetTable();
174 switch ( $target ) {
175 case 'revision':
176 return [ 'rev_id' ];
177 case 'ip_changes':
178 return [ 'ipc_rev_id' ];
179 default:
180 wfWarn(
181 __METHOD__ . ": Unknown value '$target' from " . static::class . '::getTargetTable()', 0
182 );
183 return [ 'rev_id' ];
184 }
185 }
186
193 public static function processDateFilter( array $opts ) {
194 $start = $opts['start'] ?? '';
195 $end = $opts['end'] ?? '';
196 $year = $opts['year'] ?? '';
197 $month = $opts['month'] ?? '';
198
199 if ( $start !== '' && $end !== '' && $start > $end ) {
200 $temp = $start;
201 $start = $end;
202 $end = $temp;
203 }
204
205 // If year/month legacy filtering options are set, convert them to display the new stamp
206 if ( $year !== '' || $month !== '' ) {
207 // Reuse getDateCond logic, but subtract a day because
208 // the endpoints of our date range appear inclusive
209 // but the internal end offsets are always exclusive
210 $legacyTimestamp = ReverseChronologicalPager::getOffsetDate( $year, $month );
211 $legacyDateTime = new DateTime( $legacyTimestamp->getTimestamp( TS::ISO_8601 ) );
212 $legacyDateTime = $legacyDateTime->modify( '-1 day' );
213
214 // Clear the new timestamp range options if used and
215 // replace with the converted legacy timestamp
216 $start = '';
217 $end = $legacyDateTime->format( 'Y-m-d' );
218 }
219
220 $opts['start'] = $start;
221 $opts['end'] = $end;
222
223 return $opts;
224 }
225}
226
231class_alias( ContribsPager::class, 'ContribsPager' );
232
234class_alias( ContribsPager::class, 'MediaWiki\\Pager\\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.
Factory for LinkBatch objects to batch query page metadata.
Pager for Special:Contributions.
string $target
User name, or a string describing an IP address range.
getDatabase()
Get the Database object in use.
IndexPager with a formatted navigation bar.
Service for looking up page revisions.
Pager for Special:Contributions.
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.
__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.
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.