MediaWiki  master
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 = wfMessage( '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 = wfMessage( '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 ? $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 }
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
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:1458
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.
getContext()
Get the base IContextSource object.
Pager for Special:Contributions.
static processDateFilter(array $opts)
Set up date filter options, given request data.
static isExternal( $username)
Tells whether the username is external or not.
A base class for outputting syndication feeds (e.g.
Definition: FeedItem.php:36
static stripComment( $text)
Quickie hack...
Definition: FeedItem.php:224
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.
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.
Definition: SlotRecord.php:40
Creates User objects.
Definition: UserFactory.php:38
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.
Definition: TextContent.php:40
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:638
Service for formatting and validating API parameters.
Shared interface for rigor levels when dealing with User methods.
A title parser service for MediaWiki.
Definition: TitleParser.php:33
Database cluster connection, tracking, load balancing, and transaction manager interface.
$content
Definition: router.php:76