MediaWiki master
NewPagesPager.php
Go to the documentation of this file.
1<?php
22namespace MediaWiki\Pager;
23
46use stdClass;
49
56
57 protected FormOptions $opts;
59
61 private array $formattedComments = [];
65 public $mGroupByDate = true;
66
67 private GroupPermissionsLookup $groupPermissionsLookup;
68 private HookRunner $hookRunner;
69 private LinkBatchFactory $linkBatchFactory;
70 private NamespaceInfo $namespaceInfo;
71 private ChangeTagsStore $changeTagsStore;
72 private RowCommentFormatter $rowCommentFormatter;
73 private IContentHandlerFactory $contentHandlerFactory;
74 private TempUserConfig $tempUserConfig;
75
76 public function __construct(
77 IContextSource $context,
78 LinkRenderer $linkRenderer,
79 GroupPermissionsLookup $groupPermissionsLookup,
80 HookContainer $hookContainer,
81 LinkBatchFactory $linkBatchFactory,
82 NamespaceInfo $namespaceInfo,
83 ChangeTagsStore $changeTagsStore,
84 RowCommentFormatter $rowCommentFormatter,
85 IContentHandlerFactory $contentHandlerFactory,
86 TempUserConfig $tempUserConfig,
88 ) {
89 parent::__construct( $context, $linkRenderer );
90 $this->groupPermissionsLookup = $groupPermissionsLookup;
91 $this->hookRunner = new HookRunner( $hookContainer );
92 $this->linkBatchFactory = $linkBatchFactory;
93 $this->namespaceInfo = $namespaceInfo;
94 $this->changeTagsStore = $changeTagsStore;
95 $this->rowCommentFormatter = $rowCommentFormatter;
96 $this->contentHandlerFactory = $contentHandlerFactory;
97 $this->tempUserConfig = $tempUserConfig;
98 $this->opts = $opts;
99 $this->tagsCache = new MapCacheLRU( 50 );
100 }
101
102 public function getQueryInfo() {
103 $rcQuery = RecentChange::getQueryInfo();
104
105 $conds = [];
106 $conds['rc_new'] = 1;
107
108 $username = $this->opts->getValue( 'username' );
109 $user = Title::makeTitleSafe( NS_USER, $username );
110
111 $size = abs( intval( $this->opts->getValue( 'size' ) ) );
112 if ( $size > 0 ) {
113 $db = $this->getDatabase();
114 if ( $this->opts->getValue( 'size-mode' ) === 'max' ) {
115 $conds[] = $db->expr( 'page_len', '<=', $size );
116 } else {
117 $conds[] = $db->expr( 'page_len', '>=', $size );
118 }
119 }
120
121 if ( $user ) {
122 $conds['actor_name'] = $user->getText();
123 } elseif ( $this->opts->getValue( 'hideliu' ) ) {
124 // Only include anonymous users if the 'hideliu' option has been provided.
125 $anonOnlyExpr = $this->getDatabase()->expr( 'actor_user', '=', null );
126 if ( $this->tempUserConfig->isKnown() ) {
127 $anonOnlyExpr = $anonOnlyExpr->orExpr( $this->tempUserConfig->getMatchCondition(
128 $this->getDatabase(), 'actor_name', IExpression::LIKE
129 ) );
130 }
131 $conds[] = $anonOnlyExpr;
132 }
133
134 $conds = array_merge( $conds, $this->getNamespaceCond() );
135
136 # If this user cannot see patrolled edits or they are off, don't do dumb queries!
137 if ( $this->opts->getValue( 'hidepatrolled' ) && $this->getUser()->useNPPatrol() ) {
138 $conds['rc_patrolled'] = RecentChange::PRC_UNPATROLLED;
139 }
140
141 if ( $this->opts->getValue( 'hidebots' ) ) {
142 $conds['rc_bot'] = 0;
143 }
144
145 if ( $this->opts->getValue( 'hideredirs' ) ) {
146 $conds['page_is_redirect'] = 0;
147 }
148
149 // Allow changes to the New Pages query
150 $tables = array_merge( $rcQuery['tables'], [ 'page' ] );
151 $fields = array_merge( $rcQuery['fields'], [
152 'length' => 'page_len', 'rev_id' => 'page_latest', 'page_namespace', 'page_title',
153 'page_content_model',
154 ] );
155 $join_conds = [ 'page' => [ 'JOIN', 'page_id=rc_cur_id' ] ] + $rcQuery['joins'];
156
157 $this->hookRunner->onSpecialNewpagesConditions(
158 $this, $this->opts, $conds, $tables, $fields, $join_conds );
159
160 $info = [
161 'tables' => $tables,
162 'fields' => $fields,
163 'conds' => $conds,
164 'options' => [],
165 'join_conds' => $join_conds
166 ];
167
168 // Modify query for tags
169 $this->changeTagsStore->modifyDisplayQuery(
170 $info['tables'],
171 $info['fields'],
172 $info['conds'],
173 $info['join_conds'],
174 $info['options'],
175 $this->opts['tagfilter'],
176 $this->opts['tagInvert']
177 );
178
179 return $info;
180 }
181
182 // Based on ContribsPager.php
183 private function getNamespaceCond() {
184 $namespace = $this->opts->getValue( 'namespace' );
185 if ( $namespace === 'all' || $namespace === '' ) {
186 return [];
187 }
188
189 $namespace = intval( $namespace );
190 if ( $namespace < NS_MAIN ) {
191 // Negative namespaces are invalid
192 return [];
193 }
194
195 $invert = $this->opts->getValue( 'invert' );
196 $associated = $this->opts->getValue( 'associated' );
197
198 $eq_op = $invert ? '!=' : '=';
199 $dbr = $this->getDatabase();
200 $namespaces = [ $namespace ];
201 if ( $associated ) {
202 $namespaces[] = $this->namespaceInfo->getAssociated( $namespace );
203 }
204
205 return [ $dbr->expr( 'rc_namespace', $eq_op, $namespaces ) ];
206 }
207
208 public function getIndexField() {
209 return [ [ 'rc_timestamp', 'rc_id' ] ];
210 }
211
212 public function formatRow( $row ) {
213 $title = Title::newFromRow( $row );
214
215 // Revision deletion works on revisions,
216 // so cast our recent change row to a revision row.
217 $revRecord = $this->revisionFromRcResult( $row, $title );
218
219 $classes = [];
220 $attribs = [ 'data-mw-revid' => $row->rc_this_oldid ];
221
222 $lang = $this->getLanguage();
223 $time = ChangesList::revDateLink( $revRecord, $this->getUser(), $lang, null, 'mw-newpages-time' );
224
225 $linkRenderer = $this->getLinkRenderer();
226
227 $query = $title->isRedirect() ? [ 'redirect' => 'no' ] : [];
228
229 $plink = Html::rawElement( 'bdi', [ 'dir' => $lang->getDir() ], $linkRenderer->makeKnownLink(
230 $title,
231 null,
232 [ 'class' => 'mw-newpages-pagename' ],
233 $query
234 ) );
235 $linkArr = [];
236 $linkArr[] = $linkRenderer->makeKnownLink(
237 $title,
238 $this->msg( 'hist' )->text(),
239 [ 'class' => 'mw-newpages-history' ],
240 [ 'action' => 'history' ]
241 );
242 if ( $this->contentHandlerFactory->getContentHandler( $title->getContentModel() )
243 ->supportsDirectEditing()
244 ) {
245 $linkArr[] = $linkRenderer->makeKnownLink(
246 $title,
247 $this->msg( 'editlink' )->text(),
248 [ 'class' => 'mw-newpages-edit' ],
249 [ 'action' => 'edit' ]
250 );
251 }
252 $links = $this->msg( 'parentheses' )->rawParams( $this->getLanguage()
253 ->pipeList( $linkArr ) )->escaped();
254
255 $length = Html::rawElement(
256 'span',
257 [ 'class' => 'mw-newpages-length' ],
258 $this->msg( 'brackets' )->rawParams(
259 $this->msg( 'nbytes' )->numParams( $row->length )->escaped()
260 )->escaped()
261 );
262
263 $ulink = Linker::revUserTools( $revRecord );
264 $rc = RecentChange::newFromRow( $row );
265 if ( ChangesList::userCan( $rc, RevisionRecord::DELETED_COMMENT, $this->getAuthority() ) ) {
266 $comment = $this->formattedComments[$rc->mAttribs['rc_id']];
267 } else {
268 $comment = '<span class="comment">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
269 }
270 if ( ChangesList::isDeleted( $rc, RevisionRecord::DELETED_COMMENT ) ) {
271 $deletedClass = 'history-deleted';
272 if ( ChangesList::isDeleted( $rc, RevisionRecord::DELETED_RESTRICTED ) ) {
273 $deletedClass .= ' mw-history-suppressed';
274 }
275 $comment = '<span class="' . $deletedClass . ' comment">' . $comment . '</span>';
276 }
277
278 if ( $this->getUser()->useNPPatrol() && !$row->rc_patrolled ) {
279 $classes[] = 'not-patrolled';
280 }
281
282 # Add a class for zero byte pages
283 if ( $row->length == 0 ) {
284 $classes[] = 'mw-newpages-zero-byte-page';
285 }
286
287 # Tags, if any.
288 if ( isset( $row->ts_tags ) ) {
289 [ $tagDisplay, $newClasses ] = $this->tagsCache->getWithSetCallback(
290 $this->tagsCache->makeKey(
291 $row->ts_tags,
292 $this->getUser()->getName(),
293 $lang->getCode()
294 ),
295 fn () => ChangeTags::formatSummaryRow(
296 $row->ts_tags,
297 'newpages',
298 $this->getContext()
299 )
300 );
301 $classes = array_merge( $classes, $newClasses );
302 } else {
303 $tagDisplay = '';
304 }
305
306 # Display the old title if the namespace/title has been changed
307 $oldTitleText = '';
308 $oldTitle = Title::makeTitle( $row->rc_namespace, $row->rc_title );
309
310 if ( !$title->equals( $oldTitle ) ) {
311 $oldTitleText = $oldTitle->getPrefixedText();
312 $oldTitleText = Html::rawElement(
313 'span',
314 [ 'class' => 'mw-newpages-oldtitle' ],
315 $this->msg( 'rc-old-title' )->params( $oldTitleText )->escaped()
316 );
317 }
318
319 $ret = "{$time} {$plink} {$links} {$length} {$ulink} {$comment} "
320 . "{$tagDisplay} {$oldTitleText}";
321
322 // Let extensions add data
323 $this->hookRunner->onNewPagesLineEnding(
324 $this, $ret, $row, $classes, $attribs );
325 $attribs = array_filter( $attribs,
326 [ Sanitizer::class, 'isReservedDataAttribute' ],
327 ARRAY_FILTER_USE_KEY
328 );
329
330 if ( $classes ) {
331 $attribs['class'] = $classes;
332 }
333
334 return Html::rawElement( 'li', $attribs, $ret ) . "\n";
335 }
336
342 protected function revisionFromRcResult( stdClass $result, Title $title ): RevisionRecord {
343 $revRecord = new MutableRevisionRecord( $title );
344 $revRecord->setTimestamp( $result->rc_timestamp );
345 $revRecord->setId( $result->rc_this_oldid );
346 $revRecord->setVisibility( (int)$result->rc_deleted );
347
348 $user = new UserIdentityValue(
349 (int)$result->rc_user,
350 $result->rc_user_text
351 );
352 $revRecord->setUser( $user );
353
354 return $revRecord;
355 }
356
357 protected function doBatchLookups() {
358 $linkBatch = $this->linkBatchFactory->newLinkBatch();
359 foreach ( $this->mResult as $row ) {
360 $linkBatch->addUser( new UserIdentityValue( (int)$row->rc_user, $row->rc_user_text ) );
361 $linkBatch->add( $row->page_namespace, $row->page_title );
362 }
363 $linkBatch->execute();
364
365 $this->formattedComments = $this->rowCommentFormatter->formatRows(
366 $this->mResult, 'rc_comment', 'page_namespace', 'page_title', 'rc_id', true
367 );
368 }
369
373 protected function getStartBody() {
374 return "<section class='mw-pager-body'>\n";
375 }
376
380 protected function getEndBody() {
381 return "</section>\n";
382 }
383}
384
389class_alias( NewPagesPager::class, 'NewPagesPager' );
const NS_USER
Definition Defines.php:67
const NS_MAIN
Definition Defines.php:65
Read-write access to the change_tags table.
Recent changes tagging.
This is basically a CommentFormatter with a CommentStore dependency, allowing it to retrieve comment ...
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Helper class to keep track of options when mixing links and form elements.
This class is a collection of static functions that serve two purposes:
Definition Html.php:57
Class that generates HTML for internal links.
Some internal bits split of from Skin.php.
Definition Linker.php:61
getDatabase()
Get the Database object in use.
__construct(IContextSource $context, LinkRenderer $linkRenderer, GroupPermissionsLookup $groupPermissionsLookup, HookContainer $hookContainer, LinkBatchFactory $linkBatchFactory, NamespaceInfo $namespaceInfo, ChangeTagsStore $changeTagsStore, RowCommentFormatter $rowCommentFormatter, IContentHandlerFactory $contentHandlerFactory, TempUserConfig $tempUserConfig, FormOptions $opts)
formatRow( $row)
Returns an HTML string representing the result row $row.
getEndBody()
Hook into getBody() for the end of the list.to overridestring
getIndexField()
Returns the name of the index field.
bool $mGroupByDate
Whether to group items by date by default this is disabled, but eventually the intention should be to...
revisionFromRcResult(stdClass $result, Title $title)
doBatchLookups()
Called from getBody(), before getStartBody() is called and after doQuery() was called.
getQueryInfo()
Provides all parameters needed for the main paged query.
getStartBody()
Hook into getBody(), allows text to be inserted at the start.This will be called even if there are no...
IndexPager with a formatted navigation bar.
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:46
Base class for lists of recent changes shown on special pages.
Utility class for creating and reading rows in the recentchanges table.
Page revision base class.
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
isRedirect( $flags=0)
Is this an article that is a redirect page? Uses link cache, adding it if necessary.
Definition Title.php:2616
equals(object $other)
Compares with another Title.
Definition Title.php:3109
getContentModel( $flags=0)
Get the page's content model id, see the CONTENT_MODEL_XXX constants.
Definition Title.php:1068
Value object representing a user's identity.
Store key-value entries in a size-limited in-memory LRU cache.
Interface for objects which can provide a MediaWiki context on request.
Interface for temporary user creation config and name matching.