MediaWiki REL1_39
ApiFeedContributions.php
Go to the documentation of this file.
1<?php
38
43
45 private $revisionStore;
46
48 private $titleParser;
49
51 private $linkRenderer;
52
54 private $linkBatchFactory;
55
57 private $hookContainer;
58
60 private $loadBalancer;
61
63 private $namespaceInfo;
64
66 private $actorMigration;
67
69 private $userFactory;
70
72 private $commentFormatter;
73
75 private $hookRunner;
76
91 public function __construct(
92 ApiMain $main,
93 $action,
94 RevisionStore $revisionStore,
95 TitleParser $titleParser,
96 LinkRenderer $linkRenderer,
97 LinkBatchFactory $linkBatchFactory,
98 HookContainer $hookContainer,
99 ILoadBalancer $loadBalancer,
100 NamespaceInfo $namespaceInfo,
101 ActorMigration $actorMigration,
102 UserFactory $userFactory,
103 CommentFormatter $commentFormatter
104 ) {
105 parent::__construct( $main, $action );
106 $this->revisionStore = $revisionStore;
107 $this->titleParser = $titleParser;
108 $this->linkRenderer = $linkRenderer;
109 $this->linkBatchFactory = $linkBatchFactory;
110 $this->hookContainer = $hookContainer;
111 $this->loadBalancer = $loadBalancer;
112 $this->namespaceInfo = $namespaceInfo;
113 $this->actorMigration = $actorMigration;
114 $this->userFactory = $userFactory;
115 $this->commentFormatter = $commentFormatter;
116
117 $this->hookRunner = new ApiHookRunner( $hookContainer );
118 }
119
125 public function getCustomPrinter() {
126 return new ApiFormatFeedWrapper( $this->getMain() );
127 }
128
129 public function execute() {
130 $params = $this->extractRequestParams();
131
132 $config = $this->getConfig();
133 if ( !$config->get( MainConfigNames::Feed ) ) {
134 $this->dieWithError( 'feed-unavailable' );
135 }
136
137 $feedClasses = $config->get( MainConfigNames::FeedClasses );
138 if ( !isset( $feedClasses[$params['feedformat']] ) ) {
139 $this->dieWithError( 'feed-invalid' );
140 }
141
142 if ( $params['showsizediff'] && $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
143 $this->dieWithError( 'apierror-sizediffdisabled' );
144 }
145
146 $msg = $this->msg( 'Contributions' )->inContentLanguage()->text();
147 $feedTitle = $config->get( MainConfigNames::Sitename ) . ' - ' . $msg .
148 ' [' . $config->get( MainConfigNames::LanguageCode ) . ']';
149
150 $target = $params['user'];
151 if ( ExternalUserNames::isExternal( $target ) ) {
152 // Interwiki names make invalid titles, so put the target in the query instead.
153 $feedUrl = SpecialPage::getTitleFor( 'Contributions' )->getFullURL( [ 'target' => $target ] );
154 } else {
155 $feedUrl = SpecialPage::getTitleFor( 'Contributions', $target )->getFullURL();
156 }
157
158 $feed = new $feedClasses[$params['feedformat']] (
159 $feedTitle,
160 htmlspecialchars( $msg ),
161 $feedUrl
162 );
163
164 // Convert year/month parameters to end parameter
165 $params['start'] = '';
166 $params['end'] = '';
167 $params = ContribsPager::processDateFilter( $params );
168
169 $targetUser = $this->userFactory->newFromName( $target, UserRigorOptions::RIGOR_NONE );
170
171 $pager = new ContribsPager(
172 $this->getContext(), [
173 'target' => $target,
174 'namespace' => $params['namespace'],
175 'start' => $params['start'],
176 'end' => $params['end'],
177 'tagFilter' => $params['tagfilter'],
178 'deletedOnly' => $params['deletedonly'],
179 'topOnly' => $params['toponly'],
180 'newOnly' => $params['newonly'],
181 'hideMinor' => $params['hideminor'],
182 'showSizeDiff' => $params['showsizediff'],
183 ],
184 $this->linkRenderer,
185 $this->linkBatchFactory,
186 $this->hookContainer,
187 $this->loadBalancer,
188 $this->actorMigration,
189 $this->revisionStore,
190 $this->namespaceInfo,
191 $targetUser,
192 $this->commentFormatter
193 );
194
195 $feedLimit = $this->getConfig()->get( MainConfigNames::FeedLimit );
196 if ( $pager->getLimit() > $feedLimit ) {
197 $pager->setLimit( $feedLimit );
198 }
199
200 $feedItems = [];
201 if ( $pager->getNumRows() > 0 ) {
202 $count = 0;
203 $limit = $pager->getLimit();
204 foreach ( $pager->mResult as $row ) {
205 // ContribsPager selects one more row for navigation, skip that row
206 if ( ++$count > $limit ) {
207 break;
208 }
209 $item = $this->feedItem( $row );
210 if ( $item !== null ) {
211 $feedItems[] = $item;
212 }
213 }
214 }
215
216 ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
217 }
218
219 protected function feedItem( $row ) {
220 // This hook is the api contributions equivalent to the
221 // ContributionsLineEnding hook. Hook implementers may cancel
222 // the hook to signal the user is not allowed to read this item.
223 $feedItem = null;
224 $hookResult = $this->hookRunner->onApiFeedContributions__feedItem(
225 $row, $this->getContext(), $feedItem );
226 // Hook returned a valid feed item
227 if ( $feedItem instanceof FeedItem ) {
228 return $feedItem;
229 // Hook was canceled and did not return a valid feed item
230 } elseif ( !$hookResult ) {
231 return null;
232 }
233
234 // Hook completed and did not return a valid feed item
235 $title = Title::makeTitle( (int)$row->page_namespace, $row->page_title );
236
237 if ( $title && $this->getAuthority()->authorizeRead( 'read', $title ) ) {
238 $date = $row->rev_timestamp;
239 $comments = $title->getTalkPage()->getFullURL();
240 $revision = $this->revisionStore->newRevisionFromRow( $row, 0, $title );
241
242 return new FeedItem(
243 $title->getPrefixedText(),
244 $this->feedItemDesc( $revision ),
245 $title->getFullURL( [ 'diff' => $revision->getId() ] ),
246 $date,
247 $this->feedItemAuthor( $revision ),
248 $comments
249 );
250 }
251
252 return null;
253 }
254
260 protected function feedItemAuthor( RevisionRecord $revision ) {
261 $user = $revision->getUser();
262 return $user ? $user->getName() : '';
263 }
264
270 protected function feedItemDesc( RevisionRecord $revision ) {
271 $msg = $this->msg( 'colon-separator' )->inContentLanguage()->text();
272 try {
273 $content = $revision->getContent( SlotRecord::MAIN );
274 } catch ( RevisionAccessException $e ) {
275 $content = null;
276 }
277
278 if ( $content instanceof TextContent ) {
279 // only textual content has a "source view".
280 $html = nl2br( htmlspecialchars( $content->getText(), ENT_COMPAT ) );
281 } else {
282 // XXX: we could get an HTML representation of the content via getParserOutput, but that may
283 // contain JS magic and generally may not be suitable for inclusion in a feed.
284 // Perhaps Content should have a getDescriptiveHtml method and/or a getSourceText method.
285 // Compare also FeedUtils::formatDiffRow.
286 $html = '';
287 }
288
289 $comment = $revision->getComment();
290
291 return '<p>' . htmlspecialchars( $this->feedItemAuthor( $revision ) ) . $msg .
292 htmlspecialchars( FeedItem::stripComment( $comment->text ?? '' ) ) .
293 "</p>\n<hr />\n<div>" . $html . '</div>';
294 }
295
296 public function getAllowedParams() {
297 $feedFormatNames = array_keys( $this->getConfig()->get( MainConfigNames::FeedClasses ) );
298
299 $ret = [
300 'feedformat' => [
301 ParamValidator::PARAM_DEFAULT => 'rss',
302 ParamValidator::PARAM_TYPE => $feedFormatNames
303 ],
304 'user' => [
305 ParamValidator::PARAM_TYPE => 'user',
306 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'cidr', 'id', 'interwiki' ],
307 ParamValidator::PARAM_REQUIRED => true,
308 ],
309 'namespace' => [
310 ParamValidator::PARAM_TYPE => 'namespace'
311 ],
312 'year' => [
313 ParamValidator::PARAM_TYPE => 'integer'
314 ],
315 'month' => [
316 ParamValidator::PARAM_TYPE => 'integer'
317 ],
318 'tagfilter' => [
319 ParamValidator::PARAM_ISMULTI => true,
320 ParamValidator::PARAM_TYPE => array_values( ChangeTags::listDefinedTags() ),
321 ParamValidator::PARAM_DEFAULT => '',
322 ],
323 'deletedonly' => false,
324 'toponly' => false,
325 'newonly' => false,
326 'hideminor' => false,
327 'showsizediff' => [
328 ParamValidator::PARAM_DEFAULT => false,
329 ],
330 ];
331
332 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
333 $ret['showsizediff'][ApiBase::PARAM_HELP_MSG] = 'api-help-param-disabled-in-miser-mode';
334 }
335
336 return $ret;
337 }
338
339 protected function getExamplesMessages() {
340 return [
341 'action=feedcontributions&user=Example'
342 => 'apihelp-feedcontributions-example-simple',
343 ];
344 }
345}
This is not intended to be a long-term part of MediaWiki; it will be deprecated and removed once acto...
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:56
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1454
getMain()
Get the main module.
Definition ApiBase.php:514
getResult()
Get the result object.
Definition ApiBase.php:629
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:765
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:163
__construct(ApiMain $main, $action, RevisionStore $revisionStore, TitleParser $titleParser, LinkRenderer $linkRenderer, LinkBatchFactory $linkBatchFactory, HookContainer $hookContainer, ILoadBalancer $loadBalancer, NamespaceInfo $namespaceInfo, ActorMigration $actorMigration, UserFactory $userFactory, CommentFormatter $commentFormatter)
feedItemDesc(RevisionRecord $revision)
feedItemAuthor(RevisionRecord $revision)
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
getCustomPrinter()
This module uses a custom feed wrapper printer.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
getExamplesMessages()
Returns usage examples for this module.
This printer is used to wrap an instance of the Feed class.
static setResult( $result, $feed, $feedItems)
Call this method to initialize output data.
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:52
static listDefinedTags()
Basically lists defined tags which count even if they aren't applied to anything.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
getContext()
Get the base IContextSource object.
Pager for Special:Contributions.
A base class for outputting syndication feeds (e.g.
Definition FeedItem.php:36
This class provides an implementation of the hook interfaces used by the core Action API,...
This is the main service interface for converting single-line comments from various DB comment fields...
Class that generates HTML anchor link elements for pages.
A class containing constants representing the names of configuration variables.
Type definition for user types.
Definition UserDef.php:27
Exception representing a failure to look up a revision.
Page revision base class.
getUser( $audience=self::FOR_PUBLIC, Authority $performer=null)
Fetch revision's author's user identity, if it's available to the specified audience.
getComment( $audience=self::FOR_PUBLIC, Authority $performer=null)
Fetch revision comment, if it's available to the specified audience.
getContent( $role, $audience=self::FOR_PUBLIC, Authority $performer=null)
Returns the Content of the given slot of this revision.
Service for looking up page revisions.
Value object representing a content slot associated with a page revision.
Creates User objects.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Content object implementation for representing flat text.
Service for formatting and validating API parameters.
Shared interface for rigor levels when dealing with User methods.
A title parser service for MediaWiki.
Create and track the database connections and transactions for a given database cluster.
$content
Definition router.php:76