MediaWiki REL1_40
ContributionsLookup.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\Revision;
4
5use ChangeTags;
16use Message;
20
25
27 private $revisionStore;
28
30 private $linkRendererFactory;
31
33 private $linkBatchFactory;
34
36 private $hookContainer;
37
39 private $loadBalancer;
40
42 private $actorMigration;
43
45 private $namespaceInfo;
46
48 private $commentFormatter;
49
60 public function __construct(
61 RevisionStore $revisionStore,
62 LinkRendererFactory $linkRendererFactory,
63 LinkBatchFactory $linkBatchFactory,
64 HookContainer $hookContainer,
65 ILoadBalancer $loadBalancer,
66 ActorMigration $actorMigration,
67 NamespaceInfo $namespaceInfo,
68 CommentFormatter $commentFormatter
69 ) {
70 $this->revisionStore = $revisionStore;
71 $this->linkRendererFactory = $linkRendererFactory;
72 $this->linkBatchFactory = $linkBatchFactory;
73 $this->hookContainer = $hookContainer;
74 $this->loadBalancer = $loadBalancer;
75 $this->actorMigration = $actorMigration;
76 $this->namespaceInfo = $namespaceInfo;
77 $this->commentFormatter = $commentFormatter;
78 }
79
91 private function getPagerParams( int $limit, string $segment ): array {
92 $dir = 'next';
93 $seg = explode( '|', $segment, 2 );
94 if ( count( $seg ) > 1 ) {
95 if ( $seg[0] === 'after' ) {
96 $dir = 'prev';
97 $segment = $seg[1];
98 } elseif ( $seg[0] == 'before' ) {
99 $segment = $seg[1];
100 } else {
101 $dir = null;
102 $segment = null;
103 }
104 } else {
105 $segment = null;
106 }
107 return [
108 'limit' => $limit,
109 'offset' => $segment,
110 'dir' => $dir
111 ];
112 }
113
124 public function getContributions(
125 UserIdentity $target,
126 int $limit,
127 Authority $performer,
128 string $segment = '',
129 string $tag = null
131 $context = new RequestContext();
132 $context->setAuthority( $performer );
133
134 $paramArr = $this->getPagerParams( $limit, $segment );
135 $context->setRequest( new FauxRequest( $paramArr ) );
136
137 // TODO: explore moving this to factory method for testing
138 $pager = $this->getContribsPager( $context, $target, [
139 'tagfilter' => $tag,
140 'revisionsOnly' => true
141 ] );
142 $revisions = [];
143 $tags = [];
144 $count = 0;
145 if ( $pager->getNumRows() > 0 ) {
146 foreach ( $pager->mResult as $row ) {
147 // We retrieve and ignore one extra record to see if we are on the oldest segment.
148 if ( ++$count > $limit ) {
149 break;
150 }
151
152 // TODO: pre-load title batch?
153 $revision = $this->revisionStore->newRevisionFromRow( $row, 0 );
154 $revisions[] = $revision;
155 if ( $row->ts_tags ) {
156 $tagNames = explode( ',', $row->ts_tags );
157 $tags[ $row->rev_id ] = $this->getContributionTags( $tagNames );
158 }
159 }
160 }
161
162 $deltas = $this->getContributionDeltas( $revisions );
163
164 $flags = [
165 'newest' => $pager->mIsFirst,
166 'oldest' => $pager->mIsLast,
167 ];
168
169 // TODO: Make me an option in IndexPager
170 $pager->mIsFirst = false; // XXX: nasty...
171 $pagingQueries = $pager->getPagingQueries();
172
173 $prev = $pagingQueries['prev']['offset'] ?? null;
174 $next = $pagingQueries['next']['offset'] ?? null;
175
176 $after = $prev ? 'after|' . $prev : null; // later in time
177 $before = $next ? 'before|' . $next : null; // earlier in time
178
179 // TODO: Possibly return public $pager properties to segment for populating URLS ($mIsFirst, $mIsLast)
180 // HACK: Force result set order to be descending. Sorting logic in ContribsPager::reallyDoQuery is confusing.
181 if ( $paramArr['dir'] === 'prev' ) {
182 $revisions = array_reverse( $revisions );
183 }
184 return new ContributionsSegment( $revisions, $tags, $before, $after, $deltas, $flags );
185 }
186
191 private function getContributionTags( array $tagNames ): array {
192 $tagMetadata = [];
193 foreach ( $tagNames as $name ) {
194 $tagDisplay = ChangeTags::tagShortDescriptionMessage( $name, RequestContext::getMain() );
195 if ( $tagDisplay ) {
196 $tagMetadata[$name] = $tagDisplay;
197 }
198 }
199 return $tagMetadata;
200 }
201
209 private function getContributionDeltas( $revisions ): array {
210 // SpecialContributions uses the size of the revision if the parent revision is unknown. Cases include:
211 // - revision has been deleted
212 // - parent rev id has not been populated (this is the case for very old revisions)
213 $parentIds = [];
214 foreach ( $revisions as $revision ) {
215 $revId = $revision->getId();
216 $parentIds[$revId] = $revision->getParentId();
217 }
218 $parentSizes = $this->revisionStore->getRevisionSizes( $parentIds );
219 $deltas = [];
220 foreach ( $revisions as $revision ) {
221 $parentId = $revision->getParentId();
222 if ( $parentId === 0 ) { // first revision on a page
223 $delta = $revision->getSize();
224 } elseif ( !isset( $parentSizes[$parentId] ) ) { // parent revision is either deleted or untracked
225 $delta = null;
226 } else {
227 $delta = $revision->getSize() - $parentSizes[$parentId];
228 }
229 $deltas[ $revision->getId() ] = $delta;
230 }
231 return $deltas;
232 }
233
243 public function getContributionCount( UserIdentity $user, Authority $performer, $tag = null ): int {
244 $context = new RequestContext();
245 $context->setAuthority( $performer );
246 $context->setRequest( new FauxRequest( [] ) );
247
248 // TODO: explore moving this to factory method for testing
249 $pager = $this->getContribsPager( $context, $user, [
250 'tagfilter' => $tag,
251 ] );
252
253 $query = $pager->getQueryInfo();
254
255 $count = $pager->mDb->selectField(
256 $query['tables'],
257 'COUNT(*)',
258 $query['conds'],
259 __METHOD__,
260 [],
261 $query['join_conds']
262 );
263
264 return (int)$count;
265 }
266
267 private function getContribsPager(
268 IContextSource $context,
269 UserIdentity $targetUser,
270 array $options
271 ) {
272 return new ContribsPager(
273 $context,
274 $options,
275 $this->linkRendererFactory->create(),
276 $this->linkBatchFactory,
277 $this->hookContainer,
278 $this->loadBalancer,
279 $this->actorMigration,
280 $this->revisionStore,
281 $this->namespaceInfo,
282 $targetUser,
283 $this->commentFormatter
284 );
285 }
286
287}
static tagShortDescriptionMessage( $tag, MessageLocalizer $context)
Get the message object for the tag's short description.
Pager for Special:Contributions.
This is the main service interface for converting single-line comments from various DB comment fields...
Factory to create LinkRender objects.
WebRequest clone which takes values from a provided array.
getContributions(UserIdentity $target, int $limit, Authority $performer, string $segment='', string $tag=null)
getContributionCount(UserIdentity $user, Authority $performer, $tag=null)
Returns the number of edits by the given user.
__construct(RevisionStore $revisionStore, LinkRendererFactory $linkRendererFactory, LinkBatchFactory $linkBatchFactory, HookContainer $hookContainer, ILoadBalancer $loadBalancer, ActorMigration $actorMigration, NamespaceInfo $namespaceInfo, CommentFormatter $commentFormatter)
Service for looking up page revisions.
This is not intended to be a long-term part of MediaWiki; it will be deprecated and removed once acto...
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Group all the pieces relevant to the context of a request into one instance.
setAuthority(Authority $authority)
Interface for objects which can provide a MediaWiki context on request.
This interface represents the authority associated the current execution context, such as a web reque...
Definition Authority.php:37
Interface for objects representing user identity.
This class is a delegate to ILBFactory for a given database cluster.