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