MediaWiki REL1_37
FeedUtils.php
Go to the documentation of this file.
1<?php
27
33class FeedUtils {
34
44 public static function checkFeedOutput( $type, $output = null ) {
45 global $wgFeed, $wgFeedClasses;
46
47 if ( $output === null ) {
48 // Todo update GoogleNewsSitemap and deprecate
49 global $wgOut;
50 $output = $wgOut;
51 }
52
53 if ( !$wgFeed ) {
54 $output->addWikiMsg( 'feed-unavailable' );
55 return false;
56 }
57
58 if ( !isset( $wgFeedClasses[$type] ) ) {
59 $output->addWikiMsg( 'feed-invalid' );
60 return false;
61 }
62
63 return true;
64 }
65
73 public static function formatDiff( $row ) {
74 $titleObj = Title::makeTitle( $row->rc_namespace, $row->rc_title );
75 $timestamp = wfTimestamp( TS_MW, $row->rc_timestamp );
76 $actiontext = '';
77 if ( $row->rc_type == RC_LOG ) {
78 $rcRow = (array)$row; // newFromRow() only accepts arrays for RC rows
79 $actiontext = LogFormatter::newFromRow( $rcRow )->getActionText();
80 }
81 return self::formatDiffRow( $titleObj,
82 $row->rc_last_oldid, $row->rc_this_oldid,
83 $timestamp,
84 $row->rc_deleted & RevisionRecord::DELETED_COMMENT
85 ? wfMessage( 'rev-deleted-comment' )->escaped()
86 : CommentStore::getStore()->getComment( 'rc_comment', $row )->text,
87 $actiontext
88 );
89 }
90
102 public static function formatDiffRow( $title, $oldid, $newid, $timestamp,
103 $comment, $actiontext = ''
104 ) {
105 global $wgFeedDiffCutoff;
106
107 // log entries
108 $unwrappedText = implode(
109 ' ',
110 array_filter( [ $actiontext, Linker::formatComment( $comment ) ] )
111 );
112 $completeText = Html::rawElement( 'p', [], $unwrappedText ) . "\n";
113
114 // NOTE: Check permissions for anonymous users, not current user.
115 // No "privileged" version should end up in the cache.
116 // Most feed readers will not log in anyway.
117 $anon = new User();
118 $services = MediaWikiServices::getInstance();
119 $permManager = $services->getPermissionManager();
120 $accErrors = $permManager->getPermissionErrors(
121 'read',
122 $anon,
123 $title
124 );
125
126 // Can't diff special pages, unreadable pages or pages with no new revision
127 // to compare against: just return the text.
128 if ( $title->getNamespace() < 0 || $accErrors || !$newid ) {
129 return $completeText;
130 }
131
132 $revLookup = $services->getRevisionLookup();
133 $contentHandlerFactory = $services->getContentHandlerFactory();
134 if ( $oldid ) {
135 $diffText = '';
136 // Don't bother generating the diff if we won't be able to show it
137 if ( $wgFeedDiffCutoff > 0 ) {
138 $revRecord = $revLookup->getRevisionById( $oldid );
139
140 if ( !$revRecord ) {
141 $diffText = false;
142 } else {
143 $mainContext = RequestContext::getMain();
144 $context = clone RequestContext::getMain();
145 $context->setTitle( $title );
146
147 $model = $revRecord->getSlot(
148 SlotRecord::MAIN,
149 RevisionRecord::RAW
150 )->getModel();
151 $contentHandler = $contentHandlerFactory->getContentHandler( $model );
152 $de = $contentHandler->createDifferenceEngine( $context, $oldid, $newid );
153 $lang = $mainContext->getLanguage();
154 $user = $mainContext->getUser();
155 $diffText = $de->getDiff(
156 $mainContext->msg( 'previousrevision' )->text(), // hack
157 $mainContext->msg( 'revisionasof',
158 $lang->userTimeAndDate( $timestamp, $user ),
159 $lang->userDate( $timestamp, $user ),
160 $lang->userTime( $timestamp, $user ) )->text() );
161 }
162 }
163
164 if ( $wgFeedDiffCutoff <= 0 || ( strlen( $diffText ) > $wgFeedDiffCutoff ) ) {
165 // Omit large diffs
166 $diffText = self::getDiffLink( $title, $newid, $oldid );
167 } elseif ( $diffText === false ) {
168 // Error in diff engine, probably a missing revision
169 $diffText = Html::rawElement(
170 'p',
171 [],
172 "Can't load revision $newid"
173 );
174 } else {
175 // Diff output fine, clean up any illegal UTF-8
176 $diffText = UtfNormal\Validator::cleanUp( $diffText );
177 $diffText = self::applyDiffStyle( $diffText );
178 }
179 } else {
180 $revRecord = $revLookup->getRevisionById( $newid );
181 if ( $wgFeedDiffCutoff <= 0 || $revRecord === null ) {
182 $newContent = $contentHandlerFactory
183 ->getContentHandler( $title->getContentModel() )
184 ->makeEmptyContent();
185 } else {
186 $newContent = $revRecord->getContent( SlotRecord::MAIN );
187 }
188
189 if ( $newContent instanceof TextContent ) {
190 // only textual content has a "source view".
191 $text = $newContent->getText();
192
193 if ( $wgFeedDiffCutoff <= 0 || strlen( $text ) > $wgFeedDiffCutoff ) {
194 $html = null;
195 } else {
196 $html = nl2br( htmlspecialchars( $text ) );
197 }
198 } else {
199 // XXX: we could get an HTML representation of the content via getParserOutput, but that may
200 // contain JS magic and generally may not be suitable for inclusion in a feed.
201 // Perhaps Content should have a getDescriptiveHtml method and/or a getSourceText method.
202 // Compare also ApiFeedContributions::feedItemDesc
203 $html = null;
204 }
205
206 if ( $html === null ) {
207 // Omit large new page diffs, T31110
208 // Also use diff link for non-textual content
209 $diffText = self::getDiffLink( $title, $newid );
210 } else {
211 $diffText = Html::rawElement(
212 'p',
213 [],
214 Html::rawElement( 'b', [], wfMessage( 'newpage' )->text() )
215 );
216 $diffText .= Html::rawElement( 'div', [], $html );
217 }
218 }
219 $completeText .= $diffText;
220
221 return $completeText;
222 }
223
233 protected static function getDiffLink( Title $title, $newid, $oldid = null ) {
234 $queryParameters = [ 'diff' => $newid ];
235 if ( $oldid != null ) {
236 $queryParameters['oldid'] = $oldid;
237 }
238 $diffUrl = $title->getFullURL( $queryParameters );
239
240 $diffLink = Html::element( 'a', [ 'href' => $diffUrl ],
241 wfMessage( 'showdiff' )->inContentLanguage()->text() );
242
243 return $diffLink;
244 }
245
254 public static function applyDiffStyle( $text ) {
255 $styles = [
256 'diff' => 'background-color: #fff; color: #202122;',
257 'diff-otitle' => 'background-color: #fff; color: #202122; text-align: center;',
258 'diff-ntitle' => 'background-color: #fff; color: #202122; text-align: center;',
259 'diff-addedline' => 'color: #202122; font-size: 88%; border-style: solid; '
260 . 'border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; '
261 . 'vertical-align: top; white-space: pre-wrap;',
262 'diff-deletedline' => 'color: #202122; font-size: 88%; border-style: solid; '
263 . 'border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; '
264 . 'vertical-align: top; white-space: pre-wrap;',
265 'diff-context' => 'background-color: #f8f9fa; color: #202122; font-size: 88%; '
266 . 'border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; '
267 . 'border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;',
268 'diffchange' => 'font-weight: bold; text-decoration: none;',
269 ];
270
271 foreach ( $styles as $class => $style ) {
272 $text = preg_replace( '/(<\w+\b[^<>]*)\bclass=([\'"])(?:[^\'"]*\s)?' .
273 preg_quote( $class ) . '(?:\s[^\'"]*)?\2(?=[^<>]*>)/',
274 '$1style="' . $style . '"', $text );
275 }
276
277 return $text;
278 }
279
280}
$wgFeedDiffCutoff
When generating Recentchanges RSS/Atom feed, diffs will not be generated for pages larger than this s...
$wgFeedClasses
Available feeds objects.
$wgFeed
Provide syndication feeds (RSS, Atom) for, e.g., Recentchanges, Newpages.
const RC_LOG
Definition Defines.php:117
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.
$wgOut
Definition Setup.php:836
Helper functions for feeds.
Definition FeedUtils.php:33
static formatDiff( $row)
Format a diff for the newsfeed.
Definition FeedUtils.php:73
static formatDiffRow( $title, $oldid, $newid, $timestamp, $comment, $actiontext='')
Really format a diff for the newsfeed.
static applyDiffStyle( $text)
Hacky application of diff styles for the feeds.
static getDiffLink(Title $title, $newid, $oldid=null)
Generates a diff link.
static checkFeedOutput( $type, $output=null)
Check whether feeds can be used and that $type is a valid feed type.
Definition FeedUtils.php:44
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:1372
static newFromRow( $row)
Handy shortcut for constructing a formatter directly from database row.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Page revision base class.
Value object representing a content slot associated with a page revision.
Content object implementation for representing flat text.
Represents a title within MediaWiki.
Definition Title.php:48
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:69
if(!isset( $args[0])) $lang