MediaWiki REL1_40
ApiFeedContributions.php
Go to the documentation of this file.
1<?php
41
46
48 private $revisionStore;
49
51 private $titleParser;
52
54 private $linkRenderer;
55
57 private $linkBatchFactory;
58
60 private $hookContainer;
61
63 private $loadBalancer;
64
66 private $namespaceInfo;
67
69 private $actorMigration;
70
72 private $userFactory;
73
75 private $commentFormatter;
76
78 private $hookRunner;
79
94 public function __construct(
95 ApiMain $main,
96 $action,
97 RevisionStore $revisionStore,
98 TitleParser $titleParser,
99 LinkRenderer $linkRenderer,
100 LinkBatchFactory $linkBatchFactory,
101 HookContainer $hookContainer,
102 ILoadBalancer $loadBalancer,
103 NamespaceInfo $namespaceInfo,
104 ActorMigration $actorMigration,
105 UserFactory $userFactory,
106 CommentFormatter $commentFormatter
107 ) {
108 parent::__construct( $main, $action );
109 $this->revisionStore = $revisionStore;
110 $this->titleParser = $titleParser;
111 $this->linkRenderer = $linkRenderer;
112 $this->linkBatchFactory = $linkBatchFactory;
113 $this->hookContainer = $hookContainer;
114 $this->loadBalancer = $loadBalancer;
115 $this->namespaceInfo = $namespaceInfo;
116 $this->actorMigration = $actorMigration;
117 $this->userFactory = $userFactory;
118 $this->commentFormatter = $commentFormatter;
119
120 $this->hookRunner = new ApiHookRunner( $hookContainer );
121 }
122
128 public function getCustomPrinter() {
129 return new ApiFormatFeedWrapper( $this->getMain() );
130 }
131
132 public function execute() {
133 $params = $this->extractRequestParams();
134
135 $config = $this->getConfig();
136 if ( !$config->get( MainConfigNames::Feed ) ) {
137 $this->dieWithError( 'feed-unavailable' );
138 }
139
140 $feedClasses = $config->get( MainConfigNames::FeedClasses );
141 if ( !isset( $feedClasses[$params['feedformat']] ) ) {
142 $this->dieWithError( 'feed-invalid' );
143 }
144
145 if ( $params['showsizediff'] && $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
146 $this->dieWithError( 'apierror-sizediffdisabled' );
147 }
148
149 $msg = $this->msg( 'Contributions' )->inContentLanguage()->text();
150 $feedTitle = $config->get( MainConfigNames::Sitename ) . ' - ' . $msg .
151 ' [' . $config->get( MainConfigNames::LanguageCode ) . ']';
152
153 $target = $params['user'];
154 if ( ExternalUserNames::isExternal( $target ) ) {
155 // Interwiki names make invalid titles, so put the target in the query instead.
156 $feedUrl = SpecialPage::getTitleFor( 'Contributions' )->getFullURL( [ 'target' => $target ] );
157 } else {
158 $feedUrl = SpecialPage::getTitleFor( 'Contributions', $target )->getFullURL();
159 }
160
161 $feed = new $feedClasses[$params['feedformat']] (
162 $feedTitle,
163 htmlspecialchars( $msg ),
164 $feedUrl
165 );
166
167 // Convert year/month parameters to end parameter
168 $params['start'] = '';
169 $params['end'] = '';
170 $params = ContribsPager::processDateFilter( $params );
171
172 $targetUser = $this->userFactory->newFromName( $target, UserRigorOptions::RIGOR_NONE );
173
174 $pager = new ContribsPager(
175 $this->getContext(), [
176 'target' => $target,
177 'namespace' => $params['namespace'],
178 'start' => $params['start'],
179 'end' => $params['end'],
180 'tagFilter' => $params['tagfilter'],
181 'deletedOnly' => $params['deletedonly'],
182 'topOnly' => $params['toponly'],
183 'newOnly' => $params['newonly'],
184 'hideMinor' => $params['hideminor'],
185 'showSizeDiff' => $params['showsizediff'],
186 ],
187 $this->linkRenderer,
188 $this->linkBatchFactory,
189 $this->hookContainer,
190 $this->loadBalancer,
191 $this->actorMigration,
192 $this->revisionStore,
193 $this->namespaceInfo,
194 $targetUser,
195 $this->commentFormatter
196 );
197
198 $feedLimit = $this->getConfig()->get( MainConfigNames::FeedLimit );
199 if ( $pager->getLimit() > $feedLimit ) {
200 $pager->setLimit( $feedLimit );
201 }
202
203 $feedItems = [];
204 if ( $pager->getNumRows() > 0 ) {
205 $count = 0;
206 $limit = $pager->getLimit();
207 foreach ( $pager->mResult as $row ) {
208 // ContribsPager selects one more row for navigation, skip that row
209 if ( ++$count > $limit ) {
210 break;
211 }
212 $item = $this->feedItem( $row );
213 if ( $item !== null ) {
214 $feedItems[] = $item;
215 }
216 }
217 }
218
219 ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
220 }
221
222 protected function feedItem( $row ) {
223 // This hook is the api contributions equivalent to the
224 // ContributionsLineEnding hook. Hook implementers may cancel
225 // the hook to signal the user is not allowed to read this item.
226 $feedItem = null;
227 $hookResult = $this->hookRunner->onApiFeedContributions__feedItem(
228 $row, $this->getContext(), $feedItem );
229 // Hook returned a valid feed item
230 if ( $feedItem instanceof FeedItem ) {
231 return $feedItem;
232 // Hook was canceled and did not return a valid feed item
233 } elseif ( !$hookResult ) {
234 return null;
235 }
236
237 // Hook completed and did not return a valid feed item
238 $title = Title::makeTitle( (int)$row->page_namespace, $row->page_title );
239
240 if ( $title && $this->getAuthority()->authorizeRead( 'read', $title ) ) {
241 $date = $row->rev_timestamp;
242 $comments = $title->getTalkPage()->getFullURL();
243 $revision = $this->revisionStore->newRevisionFromRow( $row, 0, $title );
244
245 return new FeedItem(
246 $title->getPrefixedText(),
247 $this->feedItemDesc( $revision ),
248 $title->getFullURL( [ 'diff' => $revision->getId() ] ),
249 $date,
250 $this->feedItemAuthor( $revision ),
251 $comments
252 );
253 }
254
255 return null;
256 }
257
263 protected function feedItemAuthor( RevisionRecord $revision ) {
264 $user = $revision->getUser();
265 return $user ? $user->getName() : '';
266 }
267
273 protected function feedItemDesc( RevisionRecord $revision ) {
274 $msg = $this->msg( 'colon-separator' )->inContentLanguage()->text();
275 try {
276 $content = $revision->getContent( SlotRecord::MAIN );
277 } catch ( RevisionAccessException $e ) {
278 $content = null;
279 }
280
281 if ( $content instanceof TextContent ) {
282 // only textual content has a "source view".
283 $html = nl2br( htmlspecialchars( $content->getText(), ENT_COMPAT ) );
284 } else {
285 // XXX: we could get an HTML representation of the content via getParserOutput, but that may
286 // contain JS magic and generally may not be suitable for inclusion in a feed.
287 // Perhaps Content should have a getDescriptiveHtml method and/or a getSourceText method.
288 // Compare also MediaWiki\Feed\FeedUtils::formatDiffRow.
289 $html = '';
290 }
291
292 $comment = $revision->getComment();
293
294 return '<p>' . htmlspecialchars( $this->feedItemAuthor( $revision ) ) . $msg .
295 htmlspecialchars( FeedItem::stripComment( $comment->text ?? '' ) ) .
296 "</p>\n<hr />\n<div>" . $html . '</div>';
297 }
298
299 public function getAllowedParams() {
300 $feedFormatNames = array_keys( $this->getConfig()->get( MainConfigNames::FeedClasses ) );
301
302 $ret = [
303 'feedformat' => [
304 ParamValidator::PARAM_DEFAULT => 'rss',
305 ParamValidator::PARAM_TYPE => $feedFormatNames
306 ],
307 'user' => [
308 ParamValidator::PARAM_TYPE => 'user',
309 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'cidr', 'id', 'interwiki' ],
310 ParamValidator::PARAM_REQUIRED => true,
311 ],
312 'namespace' => [
313 ParamValidator::PARAM_TYPE => 'namespace'
314 ],
315 'year' => [
316 ParamValidator::PARAM_TYPE => 'integer'
317 ],
318 'month' => [
319 ParamValidator::PARAM_TYPE => 'integer'
320 ],
321 'tagfilter' => [
322 ParamValidator::PARAM_ISMULTI => true,
323 ParamValidator::PARAM_TYPE => array_values( ChangeTags::listDefinedTags() ),
324 ParamValidator::PARAM_DEFAULT => '',
325 ],
326 'deletedonly' => false,
327 'toponly' => false,
328 'newonly' => false,
329 'hideminor' => false,
330 'showsizediff' => [
331 ParamValidator::PARAM_DEFAULT => false,
332 ],
333 ];
334
335 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
336 $ret['showsizediff'][ApiBase::PARAM_HELP_MSG] = 'api-help-param-disabled-in-miser-mode';
337 }
338
339 return $ret;
340 }
341
342 protected function getExamplesMessages() {
343 return [
344 'action=feedcontributions&user=Example'
345 => 'apihelp-feedcontributions-example-simple',
346 ];
347 }
348}
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:59
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1460
getMain()
Get the main module.
Definition ApiBase.php:522
getResult()
Get the result object.
Definition ApiBase.php:637
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:773
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:166
__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:58
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.
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...
A base class for outputting syndication feeds (e.g.
Definition FeedItem.php:40
Class that generates HTML for internal links.
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.
Represents a title within MediaWiki.
Definition Title.php:82
This is not intended to be a long-term part of MediaWiki; it will be deprecated and removed once acto...
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.
This class is a delegate to ILBFactory for a given database cluster.
$content
Definition router.php:76