MediaWiki master
ApiFeedContributions.php
Go to the documentation of this file.
1<?php
44
49
50 private RevisionStore $revisionStore;
51 private LinkRenderer $linkRenderer;
52 private LinkBatchFactory $linkBatchFactory;
53 private HookContainer $hookContainer;
54 private IConnectionProvider $dbProvider;
55 private NamespaceInfo $namespaceInfo;
56 private UserFactory $userFactory;
57 private CommentFormatter $commentFormatter;
58 private ApiHookRunner $hookRunner;
59
72 public function __construct(
73 ApiMain $main,
74 $action,
75 RevisionStore $revisionStore,
76 LinkRenderer $linkRenderer,
77 LinkBatchFactory $linkBatchFactory,
78 HookContainer $hookContainer,
79 IConnectionProvider $dbProvider,
80 NamespaceInfo $namespaceInfo,
81 UserFactory $userFactory,
82 CommentFormatter $commentFormatter
83 ) {
84 parent::__construct( $main, $action );
85 $this->revisionStore = $revisionStore;
86 $this->linkRenderer = $linkRenderer;
87 $this->linkBatchFactory = $linkBatchFactory;
88 $this->hookContainer = $hookContainer;
89 $this->dbProvider = $dbProvider;
90 $this->namespaceInfo = $namespaceInfo;
91 $this->userFactory = $userFactory;
92 $this->commentFormatter = $commentFormatter;
93
94 $this->hookRunner = new ApiHookRunner( $hookContainer );
95 }
96
102 public function getCustomPrinter() {
103 return new ApiFormatFeedWrapper( $this->getMain() );
104 }
105
106 public function execute() {
107 $params = $this->extractRequestParams();
108
109 $config = $this->getConfig();
110 if ( !$config->get( MainConfigNames::Feed ) ) {
111 $this->dieWithError( 'feed-unavailable' );
112 }
113
114 $feedClasses = $config->get( MainConfigNames::FeedClasses );
115 if ( !isset( $feedClasses[$params['feedformat']] ) ) {
116 $this->dieWithError( 'feed-invalid' );
117 }
118
119 if ( $params['showsizediff'] && $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
120 $this->dieWithError( 'apierror-sizediffdisabled' );
121 }
122
123 $msg = $this->msg( 'Contributions' )->inContentLanguage()->text();
124 $feedTitle = $config->get( MainConfigNames::Sitename ) . ' - ' . $msg .
125 ' [' . $config->get( MainConfigNames::LanguageCode ) . ']';
126
127 $target = $params['user'];
128 if ( ExternalUserNames::isExternal( $target ) ) {
129 // Interwiki names make invalid titles, so put the target in the query instead.
130 $feedUrl = SpecialPage::getTitleFor( 'Contributions' )->getFullURL( [ 'target' => $target ] );
131 } else {
132 $feedUrl = SpecialPage::getTitleFor( 'Contributions', $target )->getFullURL();
133 }
134
135 $feed = new $feedClasses[$params['feedformat']] (
136 $feedTitle,
137 htmlspecialchars( $msg ),
138 $feedUrl
139 );
140
141 // Convert year/month parameters to end parameter
142 $params['start'] = '';
143 $params['end'] = '';
144 $params = ContribsPager::processDateFilter( $params );
145
146 $targetUser = $this->userFactory->newFromName( $target, UserRigorOptions::RIGOR_NONE );
147
148 $pager = new ContribsPager(
149 $this->getContext(), [
150 'target' => $target,
151 'namespace' => $params['namespace'],
152 'start' => $params['start'],
153 'end' => $params['end'],
154 'tagFilter' => $params['tagfilter'],
155 'deletedOnly' => $params['deletedonly'],
156 'topOnly' => $params['toponly'],
157 'newOnly' => $params['newonly'],
158 'hideMinor' => $params['hideminor'],
159 'showSizeDiff' => $params['showsizediff'],
160 ],
161 $this->linkRenderer,
162 $this->linkBatchFactory,
163 $this->hookContainer,
164 $this->dbProvider,
165 $this->revisionStore,
166 $this->namespaceInfo,
167 $targetUser,
168 $this->commentFormatter
169 );
170
171 $feedLimit = $this->getConfig()->get( MainConfigNames::FeedLimit );
172 if ( $pager->getLimit() > $feedLimit ) {
173 $pager->setLimit( $feedLimit );
174 }
175
176 $feedItems = [];
177 if ( $pager->getNumRows() > 0 ) {
178 $count = 0;
179 $limit = $pager->getLimit();
180 foreach ( $pager->mResult as $row ) {
181 // ContribsPager selects one more row for navigation, skip that row
182 if ( ++$count > $limit ) {
183 break;
184 }
185 $item = $this->feedItem( $row );
186 if ( $item !== null ) {
187 $feedItems[] = $item;
188 }
189 }
190 }
191
192 ApiFormatFeedWrapper::setResult( $this->getResult(), $feed, $feedItems );
193 }
194
195 protected function feedItem( $row ) {
196 // This hook is the api contributions equivalent to the
197 // ContributionsLineEnding hook. Hook implementers may cancel
198 // the hook to signal the user is not allowed to read this item.
199 $feedItem = null;
200 $hookResult = $this->hookRunner->onApiFeedContributions__feedItem(
201 $row, $this->getContext(), $feedItem );
202 // Hook returned a valid feed item
203 if ( $feedItem instanceof FeedItem ) {
204 return $feedItem;
205 // Hook was canceled and did not return a valid feed item
206 } elseif ( !$hookResult ) {
207 return null;
208 }
209
210 // Hook completed and did not return a valid feed item
211 $title = Title::makeTitle( (int)$row->page_namespace, $row->page_title );
212
213 if ( $title && $this->getAuthority()->authorizeRead( 'read', $title ) ) {
214 $date = $row->rev_timestamp;
215 $comments = $title->getTalkPage()->getFullURL();
216 $revision = $this->revisionStore->newRevisionFromRow( $row, 0, $title );
217
218 return new FeedItem(
219 $title->getPrefixedText(),
220 $this->feedItemDesc( $revision ),
221 $title->getFullURL( [ 'diff' => $revision->getId() ] ),
222 $date,
223 $this->feedItemAuthor( $revision ),
224 $comments
225 );
226 }
227
228 return null;
229 }
230
236 protected function feedItemAuthor( RevisionRecord $revision ) {
237 $user = $revision->getUser();
238 return $user ? $user->getName() : '';
239 }
240
246 protected function feedItemDesc( RevisionRecord $revision ) {
247 $msg = $this->msg( 'colon-separator' )->inContentLanguage()->text();
248 try {
249 $content = $revision->getContent( SlotRecord::MAIN );
250 } catch ( RevisionAccessException $e ) {
251 $content = null;
252 }
253
254 if ( $content instanceof TextContent ) {
255 // only textual content has a "source view".
256 $html = nl2br( htmlspecialchars( $content->getText(), ENT_COMPAT ) );
257 } else {
258 // XXX: we could get an HTML representation of the content via getParserOutput, but that may
259 // contain JS magic and generally may not be suitable for inclusion in a feed.
260 // Perhaps Content should have a getDescriptiveHtml method and/or a getSourceText method.
261 // Compare also MediaWiki\Feed\FeedUtils::formatDiffRow.
262 $html = '';
263 }
264
265 $comment = $revision->getComment();
266
267 return '<p>' . htmlspecialchars( $this->feedItemAuthor( $revision ) ) . $msg .
268 htmlspecialchars( FeedItem::stripComment( $comment->text ?? '' ) ) .
269 "</p>\n<hr />\n<div>" . $html . '</div>';
270 }
271
272 public function getAllowedParams() {
273 $feedFormatNames = array_keys( $this->getConfig()->get( MainConfigNames::FeedClasses ) );
274
275 $ret = [
276 'feedformat' => [
277 ParamValidator::PARAM_DEFAULT => 'rss',
278 ParamValidator::PARAM_TYPE => $feedFormatNames
279 ],
280 'user' => [
281 ParamValidator::PARAM_TYPE => 'user',
282 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'temp', 'cidr', 'id', 'interwiki' ],
283 ParamValidator::PARAM_REQUIRED => true,
284 ],
285 'namespace' => [
286 ParamValidator::PARAM_TYPE => 'namespace'
287 ],
288 'year' => [
289 ParamValidator::PARAM_TYPE => 'integer'
290 ],
291 'month' => [
292 ParamValidator::PARAM_TYPE => 'integer'
293 ],
294 'tagfilter' => [
295 ParamValidator::PARAM_ISMULTI => true,
296 ParamValidator::PARAM_TYPE => array_values( ChangeTags::listDefinedTags() ),
297 ParamValidator::PARAM_DEFAULT => '',
298 ],
299 'deletedonly' => false,
300 'toponly' => false,
301 'newonly' => false,
302 'hideminor' => false,
303 'showsizediff' => [
304 ParamValidator::PARAM_DEFAULT => false,
305 ],
306 ];
307
308 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
309 $ret['showsizediff'][ApiBase::PARAM_HELP_MSG] = 'api-help-param-disabled-in-miser-mode';
310 }
311
312 return $ret;
313 }
314
315 protected function getExamplesMessages() {
316 return [
317 'action=feedcontributions&user=Example'
318 => 'apihelp-feedcontributions-example-simple',
319 ];
320 }
321
322 public function getHelpUrls() {
323 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Feedcontributions';
324 }
325}
array $params
The job parameters.
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:64
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1542
getMain()
Get the main module.
Definition ApiBase.php:559
getResult()
Get the result object.
Definition ApiBase.php:680
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:820
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:171
feedItemDesc(RevisionRecord $revision)
feedItemAuthor(RevisionRecord $revision)
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
getHelpUrls()
Return links to more detailed help pages about the module.
getCustomPrinter()
This module uses a custom feed wrapper printer.
__construct(ApiMain $main, $action, RevisionStore $revisionStore, LinkRenderer $linkRenderer, LinkBatchFactory $linkBatchFactory, HookContainer $hookContainer, IConnectionProvider $dbProvider, NamespaceInfo $namespaceInfo, UserFactory $userFactory, CommentFormatter $commentFormatter)
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:66
static listDefinedTags()
Basically lists defined tags which count even if they aren't applied to anything.
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...
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
getContext()
Get the base IContextSource object.
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.
Pager for Special:Contributions.
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.
Parent class for all special pages.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Represents a title within MediaWiki.
Definition Title.php:78
Class to parse and build external user names.
Creates User objects.
Content object implementation for representing flat text.
Service for formatting and validating API parameters.
Shared interface for rigor levels when dealing with User methods.
Provide primary and replica IDatabase connections.