MediaWiki  master
FeedUtils.php
Go to the documentation of this file.
1 <?php
24 namespace MediaWiki\Feed;
25 
26 use CommentStore;
28 use Html;
29 use Linker;
30 use LogFormatter;
35 use OutputPage;
36 use RequestContext;
37 use TextContent;
38 use Title;
39 use User;
40 use UtfNormal;
41 
47 class FeedUtils {
48 
58  public static function checkFeedOutput( $type, $output = null ) {
59  $feed = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::Feed );
60  $feedClasses = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::FeedClasses );
61  if ( $output === null ) {
62  // Todo update GoogleNewsSitemap and deprecate
63  global $wgOut;
64  $output = $wgOut;
65  }
66 
67  if ( !$feed ) {
68  $output->addWikiMsg( 'feed-unavailable' );
69  return false;
70  }
71 
72  if ( !isset( $feedClasses[$type] ) ) {
73  $output->addWikiMsg( 'feed-invalid' );
74  return false;
75  }
76 
77  return true;
78  }
79 
89  public static function formatDiff( $row, $formattedComment = null ) {
90  $titleObj = Title::makeTitle( $row->rc_namespace, $row->rc_title );
91  $timestamp = wfTimestamp( TS_MW, $row->rc_timestamp );
92  $actiontext = '';
93  if ( $row->rc_type == RC_LOG ) {
94  $rcRow = (array)$row; // newFromRow() only accepts arrays for RC rows
95  $actiontext = LogFormatter::newFromRow( $rcRow )->getActionText();
96  }
97  if ( $row->rc_deleted & RevisionRecord::DELETED_COMMENT ) {
98  $formattedComment = wfMessage( 'rev-deleted-comment' )->escaped();
99  } elseif ( $formattedComment === null ) {
100  $formattedComment = Linker::formatComment(
101  CommentStore::getStore()->getComment( 'rc_comment', $row )->text );
102  }
103  return self::formatDiffRow2( $titleObj,
104  $row->rc_last_oldid, $row->rc_this_oldid,
105  $timestamp,
106  $formattedComment,
107  $actiontext
108  );
109  }
110 
124  public static function formatDiffRow( $title, $oldid, $newid, $timestamp,
125  $comment, $actiontext = ''
126  ) {
127  $formattedComment = MediaWikiServices::getInstance()->getCommentFormatter()
128  ->format( $comment );
129  return self::formatDiffRow2( $title, $oldid, $newid, $timestamp,
130  $formattedComment, $actiontext );
131  }
132 
145  public static function formatDiffRow2( $title, $oldid, $newid, $timestamp,
146  $formattedComment, $actiontext = ''
147  ) {
148  $feedDiffCutoff = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::FeedDiffCutoff );
149 
150  // log entries
151  $unwrappedText = implode(
152  ' ',
153  array_filter( [ $actiontext, $formattedComment ] )
154  );
155  $completeText = Html::rawElement( 'p', [], $unwrappedText ) . "\n";
156 
157  // NOTE: Check permissions for anonymous users, not current user.
158  // No "privileged" version should end up in the cache.
159  // Most feed readers will not log in anyway.
160  $anon = new User();
161  $services = MediaWikiServices::getInstance();
162  $permManager = $services->getPermissionManager();
163  $accErrors = $permManager->getPermissionErrors(
164  'read',
165  $anon,
166  $title
167  );
168 
169  // Can't diff special pages, unreadable pages or pages with no new revision
170  // to compare against: just return the text.
171  if ( $title->getNamespace() < 0 || $accErrors || !$newid ) {
172  return $completeText;
173  }
174 
175  $revLookup = $services->getRevisionLookup();
176  $contentHandlerFactory = $services->getContentHandlerFactory();
177  if ( $oldid ) {
178  $diffText = '';
179  // Don't bother generating the diff if we won't be able to show it
180  if ( $feedDiffCutoff > 0 ) {
181  $revRecord = $revLookup->getRevisionById( $oldid );
182 
183  if ( !$revRecord ) {
184  $diffText = false;
185  } else {
186  $context = new DerivativeContext( RequestContext::getMain() );
187  $context->setTitle( $title );
188 
189  $model = $revRecord->getSlot(
192  )->getModel();
193  $contentHandler = $contentHandlerFactory->getContentHandler( $model );
194  $de = $contentHandler->createDifferenceEngine( $context, $oldid, $newid );
195  $lang = $context->getLanguage();
196  $user = $context->getUser();
197  $diffText = $de->getDiff(
198  $context->msg( 'previousrevision' )->text(), // hack
199  $context->msg( 'revisionasof',
200  $lang->userTimeAndDate( $timestamp, $user ),
201  $lang->userDate( $timestamp, $user ),
202  $lang->userTime( $timestamp, $user ) )->text() );
203  }
204  }
205 
206  if ( $feedDiffCutoff <= 0 || ( strlen( $diffText ) > $feedDiffCutoff ) ) {
207  // Omit large diffs
208  $diffText = self::getDiffLink( $title, $newid, $oldid );
209  } elseif ( $diffText === false ) {
210  // Error in diff engine, probably a missing revision
211  $diffText = Html::rawElement(
212  'p',
213  [],
214  "Can't load revision $newid"
215  );
216  } else {
217  // Diff output fine, clean up any illegal UTF-8
218  $diffText = UtfNormal\Validator::cleanUp( $diffText );
219  $diffText = self::applyDiffStyle( $diffText );
220  }
221  } else {
222  $revRecord = $revLookup->getRevisionById( $newid );
223  if ( $feedDiffCutoff <= 0 || $revRecord === null ) {
224  $newContent = $contentHandlerFactory
225  ->getContentHandler( $title->getContentModel() )
226  ->makeEmptyContent();
227  } else {
228  $newContent = $revRecord->getContent( SlotRecord::MAIN );
229  }
230 
231  if ( $newContent instanceof TextContent ) {
232  // only textual content has a "source view".
233  $text = $newContent->getText();
234 
235  if ( $feedDiffCutoff <= 0 || strlen( $text ) > $feedDiffCutoff ) {
236  $html = null;
237  } else {
238  $html = nl2br( htmlspecialchars( $text ) );
239  }
240  } else {
241  // XXX: we could get an HTML representation of the content via getParserOutput, but that may
242  // contain JS magic and generally may not be suitable for inclusion in a feed.
243  // Perhaps Content should have a getDescriptiveHtml method and/or a getSourceText method.
244  // Compare also ApiFeedContributions::feedItemDesc
245  $html = null;
246  }
247 
248  if ( $html === null ) {
249  // Omit large new page diffs, T31110
250  // Also use diff link for non-textual content
251  $diffText = self::getDiffLink( $title, $newid );
252  } else {
253  $diffText = Html::rawElement(
254  'p',
255  [],
256  Html::rawElement( 'b', [], wfMessage( 'newpage' )->text() )
257  );
258  $diffText .= Html::rawElement( 'div', [], $html );
259  }
260  }
261  $completeText .= $diffText;
262 
263  return $completeText;
264  }
265 
275  protected static function getDiffLink( Title $title, $newid, $oldid = null ) {
276  $queryParameters = [ 'diff' => $newid ];
277  if ( $oldid != null ) {
278  $queryParameters['oldid'] = $oldid;
279  }
280  $diffUrl = $title->getFullURL( $queryParameters );
281 
282  $diffLink = Html::element( 'a', [ 'href' => $diffUrl ],
283  wfMessage( 'showdiff' )->inContentLanguage()->text() );
284 
285  return $diffLink;
286  }
287 
296  public static function applyDiffStyle( $text ) {
297  $styles = [
298  'diff' => 'background-color: #fff; color: #202122;',
299  'diff-otitle' => 'background-color: #fff; color: #202122; text-align: center;',
300  'diff-ntitle' => 'background-color: #fff; color: #202122; text-align: center;',
301  'diff-addedline' => 'color: #202122; font-size: 88%; border-style: solid; '
302  . 'border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; '
303  . 'vertical-align: top; white-space: pre-wrap;',
304  'diff-deletedline' => 'color: #202122; font-size: 88%; border-style: solid; '
305  . 'border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; '
306  . 'vertical-align: top; white-space: pre-wrap;',
307  'diff-context' => 'background-color: #f8f9fa; color: #202122; font-size: 88%; '
308  . 'border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; '
309  . 'border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;',
310  'diffchange' => 'font-weight: bold; text-decoration: none;',
311  ];
312 
313  foreach ( $styles as $class => $style ) {
314  $text = preg_replace( '/(<\w+\b[^<>]*)\bclass=([\'"])(?:[^\'"]*\s)?' .
315  preg_quote( $class ) . '(?:\s[^\'"]*)?\2(?=[^<>]*>)/',
316  '$1style="' . $style . '"', $text );
317  }
318 
319  return $text;
320  }
321 
322 }
323 
324 class_alias( FeedUtils::class, 'FeedUtils' );
const RC_LOG
Definition: Defines.php:118
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode) $wgOut
Definition: Setup.php:493
Handle database storage of comments such as edit summaries and log reasons.
static getStore()
An IContextSource implementation which will inherit context from another source but allow individual ...
This class is a collection of static functions that serve two purposes:
Definition: Html.php:51
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:236
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:214
Some internal bits split of from Skin.php.
Definition: Linker.php:41
static formatComment( $comment, $title=null, $local=false, $wikiId=null)
This function is called by all recent changes variants, by the page history, and by the user contribu...
Definition: Linker.php:1393
Implements the default log formatting.
static newFromRow( $row)
Handy shortcut for constructing a formatter directly from database row.
Helper functions for feeds.
Definition: FeedUtils.php:47
static formatDiff( $row, $formattedComment=null)
Format a diff for the newsfeed.
Definition: FeedUtils.php:89
static getDiffLink(Title $title, $newid, $oldid=null)
Generates a diff link.
Definition: FeedUtils.php:275
static formatDiffRow2( $title, $oldid, $newid, $timestamp, $formattedComment, $actiontext='')
Really really format a diff for the newsfeed.
Definition: FeedUtils.php:145
static formatDiffRow( $title, $oldid, $newid, $timestamp, $comment, $actiontext='')
Really format a diff for the newsfeed.
Definition: FeedUtils.php:124
static checkFeedOutput( $type, $output=null)
Check whether feeds can be used and that $type is a valid feed type.
Definition: FeedUtils.php:58
static applyDiffStyle( $text)
Hacky application of diff styles for the feeds.
Definition: FeedUtils.php:296
A class containing constants representing the names of configuration variables.
const FeedClasses
Name constant for the FeedClasses setting, for use with Config::get()
const Feed
Name constant for the Feed setting, for use with Config::get()
const FeedDiffCutoff
Name constant for the FeedDiffCutoff setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Page revision base class.
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40
This is one of the Core classes and should be read at least once by any new developers.
Definition: OutputPage.php:56
Group all the pieces relevant to the context of a request into one instance.
static getMain()
Get the RequestContext object associated with the main request.
Content object implementation for representing flat text.
Definition: TextContent.php:40
Represents a title within MediaWiki.
Definition: Title.php:52
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:641
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:70
if(!isset( $args[0])) $lang