MediaWiki master
NewPagesPager.php
Go to the documentation of this file.
1<?php
22namespace MediaWiki\Pager;
23
24use ChangesList;
25use ChangeTags;
26use MapCacheLRU;
45use RecentChange;
46use stdClass;
47
53
57 protected $opts;
58
60
62 private $formattedComments = [];
66 public $mGroupByDate = true;
67
68 private GroupPermissionsLookup $groupPermissionsLookup;
69 private HookRunner $hookRunner;
70 private LinkBatchFactory $linkBatchFactory;
71 private NamespaceInfo $namespaceInfo;
72 private ChangeTagsStore $changeTagsStore;
73 private RowCommentFormatter $rowCommentFormatter;
74 private IContentHandlerFactory $contentHandlerFactory;
75
88 public function __construct(
89 IContextSource $context,
90 LinkRenderer $linkRenderer,
91 GroupPermissionsLookup $groupPermissionsLookup,
92 HookContainer $hookContainer,
93 LinkBatchFactory $linkBatchFactory,
94 NamespaceInfo $namespaceInfo,
95 ChangeTagsStore $changeTagsStore,
96 RowCommentFormatter $rowCommentFormatter,
97 IContentHandlerFactory $contentHandlerFactory,
99 ) {
100 parent::__construct( $context, $linkRenderer );
101 $this->groupPermissionsLookup = $groupPermissionsLookup;
102 $this->hookRunner = new HookRunner( $hookContainer );
103 $this->linkBatchFactory = $linkBatchFactory;
104 $this->namespaceInfo = $namespaceInfo;
105 $this->changeTagsStore = $changeTagsStore;
106 $this->rowCommentFormatter = $rowCommentFormatter;
107 $this->contentHandlerFactory = $contentHandlerFactory;
108 $this->opts = $opts;
109 $this->tagsCache = new MapCacheLRU( 50 );
110 }
111
112 public function getQueryInfo() {
113 $rcQuery = RecentChange::getQueryInfo();
114
115 $conds = [];
116 $conds['rc_new'] = 1;
117
118 $username = $this->opts->getValue( 'username' );
119 $user = Title::makeTitleSafe( NS_USER, $username );
120
121 $size = abs( intval( $this->opts->getValue( 'size' ) ) );
122 if ( $size > 0 ) {
123 $db = $this->getDatabase();
124 if ( $this->opts->getValue( 'size-mode' ) === 'max' ) {
125 $conds[] = $db->expr( 'page_len', '<=', $size );
126 } else {
127 $conds[] = $db->expr( 'page_len', '>=', $size );
128 }
129 }
130
131 if ( $user ) {
132 $conds['actor_name'] = $user->getText();
133 } elseif ( $this->canAnonymousUsersCreatePages() && $this->opts->getValue( 'hideliu' ) ) {
134 # If anons cannot make new pages, don't "exclude logged in users"!
135 $conds['actor_user'] = null;
136 }
137
138 $conds = array_merge( $conds, $this->getNamespaceCond() );
139
140 # If this user cannot see patrolled edits or they are off, don't do dumb queries!
141 if ( $this->opts->getValue( 'hidepatrolled' ) && $this->getUser()->useNPPatrol() ) {
142 $conds['rc_patrolled'] = RecentChange::PRC_UNPATROLLED;
143 }
144
145 if ( $this->opts->getValue( 'hidebots' ) ) {
146 $conds['rc_bot'] = 0;
147 }
148
149 if ( $this->opts->getValue( 'hideredirs' ) ) {
150 $conds['page_is_redirect'] = 0;
151 }
152
153 // Allow changes to the New Pages query
154 $tables = array_merge( $rcQuery['tables'], [ 'page' ] );
155 $fields = array_merge( $rcQuery['fields'], [
156 'length' => 'page_len', 'rev_id' => 'page_latest', 'page_namespace', 'page_title',
157 'page_content_model',
158 ] );
159 $join_conds = [ 'page' => [ 'JOIN', 'page_id=rc_cur_id' ] ] + $rcQuery['joins'];
160
161 $this->hookRunner->onSpecialNewpagesConditions(
162 $this, $this->opts, $conds, $tables, $fields, $join_conds );
163
164 $info = [
165 'tables' => $tables,
166 'fields' => $fields,
167 'conds' => $conds,
168 'options' => [],
169 'join_conds' => $join_conds
170 ];
171
172 // Modify query for tags
173 $this->changeTagsStore->modifyDisplayQuery(
174 $info['tables'],
175 $info['fields'],
176 $info['conds'],
177 $info['join_conds'],
178 $info['options'],
179 $this->opts['tagfilter'],
180 $this->opts['tagInvert']
181 );
182
183 return $info;
184 }
185
186 private function canAnonymousUsersCreatePages() {
187 return $this->groupPermissionsLookup->groupHasPermission( '*', 'createpage' ) ||
188 $this->groupPermissionsLookup->groupHasPermission( '*', 'createtalk' );
189 }
190
191 // Based on ContribsPager.php
192 private function getNamespaceCond() {
193 $namespace = $this->opts->getValue( 'namespace' );
194 if ( $namespace === 'all' || $namespace === '' ) {
195 return [];
196 }
197
198 $namespace = intval( $namespace );
199 if ( $namespace < NS_MAIN ) {
200 // Negative namespaces are invalid
201 return [];
202 }
203
204 $invert = $this->opts->getValue( 'invert' );
205 $associated = $this->opts->getValue( 'associated' );
206
207 $eq_op = $invert ? '!=' : '=';
208 $dbr = $this->getDatabase();
209 $namespaces = [ $namespace ];
210 if ( $associated ) {
211 $namespaces[] = $this->namespaceInfo->getAssociated( $namespace );
212 }
213
214 return [ $dbr->expr( 'rc_namespace', $eq_op, $namespaces ) ];
215 }
216
217 public function getIndexField() {
218 return [ [ 'rc_timestamp', 'rc_id' ] ];
219 }
220
221 public function formatRow( $row ) {
222 $title = Title::newFromRow( $row );
223
224 // Revision deletion works on revisions,
225 // so cast our recent change row to a revision row.
226 $revRecord = $this->revisionFromRcResult( $row, $title );
227
228 $classes = [];
229 $attribs = [ 'data-mw-revid' => $row->rc_this_oldid ];
230
231 $lang = $this->getLanguage();
232 $dm = $lang->getDirMark();
233 $time = ChangesList::revDateLink( $revRecord, $this->getUser(), $lang, null, 'mw-newpages-time' );
234
235 $linkRenderer = $this->getLinkRenderer();
236
237 $query = $title->isRedirect() ? [ 'redirect' => 'no' ] : [];
238
239 $plink = $linkRenderer->makeKnownLink(
240 $title,
241 null,
242 [ 'class' => 'mw-newpages-pagename' ],
243 $query
244 );
245 $linkArr = [];
246 $linkArr[] = $linkRenderer->makeKnownLink(
247 $title,
248 $this->msg( 'hist' )->text(),
249 [ 'class' => 'mw-newpages-history' ],
250 [ 'action' => 'history' ]
251 );
252 if ( $this->contentHandlerFactory->getContentHandler( $title->getContentModel() )
253 ->supportsDirectEditing()
254 ) {
255 $linkArr[] = $linkRenderer->makeKnownLink(
256 $title,
257 $this->msg( 'editlink' )->text(),
258 [ 'class' => 'mw-newpages-edit' ],
259 [ 'action' => 'edit' ]
260 );
261 }
262 $links = $this->msg( 'parentheses' )->rawParams( $this->getLanguage()
263 ->pipeList( $linkArr ) )->escaped();
264
265 $length = Html::rawElement(
266 'span',
267 [ 'class' => 'mw-newpages-length' ],
268 $this->msg( 'brackets' )->rawParams(
269 $this->msg( 'nbytes' )->numParams( $row->length )->escaped()
270 )->escaped()
271 );
272
273 $ulink = Linker::revUserTools( $revRecord );
274 $rc = RecentChange::newFromRow( $row );
275 if ( ChangesList::userCan( $rc, RevisionRecord::DELETED_COMMENT, $this->getAuthority() ) ) {
276 $comment = $this->formattedComments[$rc->mAttribs['rc_id']];
277 } else {
278 $comment = '<span class="comment">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
279 }
280 if ( ChangesList::isDeleted( $rc, RevisionRecord::DELETED_COMMENT ) ) {
281 $deletedClass = 'history-deleted';
282 if ( ChangesList::isDeleted( $rc, RevisionRecord::DELETED_RESTRICTED ) ) {
283 $deletedClass .= ' mw-history-suppressed';
284 }
285 $comment = '<span class="' . $deletedClass . ' comment">' . $comment . '</span>';
286 }
287
288 if ( $this->getUser()->useNPPatrol() && !$row->rc_patrolled ) {
289 $classes[] = 'not-patrolled';
290 }
291
292 # Add a class for zero byte pages
293 if ( $row->length == 0 ) {
294 $classes[] = 'mw-newpages-zero-byte-page';
295 }
296
297 # Tags, if any.
298 if ( isset( $row->ts_tags ) ) {
299 [ $tagDisplay, $newClasses ] = $this->tagsCache->getWithSetCallback(
300 $this->tagsCache->makeKey(
301 $row->ts_tags,
302 $this->getUser()->getName(),
303 $lang->getCode()
304 ),
306 $row->ts_tags,
307 'newpages',
308 $this->getContext()
309 )
310 );
311 $classes = array_merge( $classes, $newClasses );
312 } else {
313 $tagDisplay = '';
314 }
315
316 # Display the old title if the namespace/title has been changed
317 $oldTitleText = '';
318 $oldTitle = Title::makeTitle( $row->rc_namespace, $row->rc_title );
319
320 if ( !$title->equals( $oldTitle ) ) {
321 $oldTitleText = $oldTitle->getPrefixedText();
322 $oldTitleText = Html::rawElement(
323 'span',
324 [ 'class' => 'mw-newpages-oldtitle' ],
325 $this->msg( 'rc-old-title' )->params( $oldTitleText )->escaped()
326 );
327 }
328
329 $ret = "{$time} {$dm}{$plink} {$links} {$dm}{$length} {$dm}{$ulink} {$comment} "
330 . "{$tagDisplay} {$oldTitleText}";
331
332 // Let extensions add data
333 $this->hookRunner->onNewPagesLineEnding(
334 $this, $ret, $row, $classes, $attribs );
335 $attribs = array_filter( $attribs,
336 [ Sanitizer::class, 'isReservedDataAttribute' ],
337 ARRAY_FILTER_USE_KEY
338 );
339
340 if ( $classes ) {
341 $attribs['class'] = $classes;
342 }
343
344 return Html::rawElement( 'li', $attribs, $ret ) . "\n";
345 }
346
352 protected function revisionFromRcResult( stdClass $result, Title $title ): RevisionRecord {
353 $revRecord = new MutableRevisionRecord( $title );
354 $revRecord->setTimestamp( $result->rc_timestamp );
355 $revRecord->setId( $result->rc_this_oldid );
356 $revRecord->setVisibility( (int)$result->rc_deleted );
357
358 $user = new UserIdentityValue(
359 (int)$result->rc_user,
360 $result->rc_user_text
361 );
362 $revRecord->setUser( $user );
363
364 return $revRecord;
365 }
366
367 protected function doBatchLookups() {
368 $linkBatch = $this->linkBatchFactory->newLinkBatch();
369 foreach ( $this->mResult as $row ) {
370 $linkBatch->add( NS_USER, $row->rc_user_text );
371 $linkBatch->add( NS_USER_TALK, $row->rc_user_text );
372 $linkBatch->add( $row->page_namespace, $row->page_title );
373 }
374 $linkBatch->execute();
375
376 $this->formattedComments = $this->rowCommentFormatter->formatRows(
377 $this->mResult, 'rc_comment', 'page_namespace', 'page_title', 'rc_id', true
378 );
379 }
380
384 protected function getStartBody() {
385 return "<section class='mw-pager-body'>\n";
386 }
387
391 protected function getEndBody() {
392 return "</section>\n";
393 }
394}
395
400class_alias( NewPagesPager::class, 'NewPagesPager' );
const NS_USER
Definition Defines.php:67
const NS_MAIN
Definition Defines.php:65
const NS_USER_TALK
Definition Defines.php:68
static formatSummaryRow( $tags, $unused, MessageLocalizer $localizer=null)
Creates HTML for the given tags.
Store key-value entries in a size-limited in-memory LRU cache.
Gateway class for change_tags table.
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:56
Class that generates HTML for internal links.
Some internal bits split of from Skin.php.
Definition Linker.php:63
getDatabase()
Get the Database object in use.
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...
__construct(IContextSource $context, LinkRenderer $linkRenderer, GroupPermissionsLookup $groupPermissionsLookup, HookContainer $hookContainer, LinkBatchFactory $linkBatchFactory, NamespaceInfo $namespaceInfo, ChangeTagsStore $changeTagsStore, RowCommentFormatter $rowCommentFormatter, IContentHandlerFactory $contentHandlerFactory, FormOptions $opts)
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
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:79
isRedirect( $flags=0)
Is this an article that is a redirect page? Uses link cache, adding it if necessary.
Definition Title.php:2623
equals(object $other)
Compares with another Title.
Definition Title.php:3107
getContentModel( $flags=0)
Get the page's content model id, see the CONTENT_MODEL_XXX constants.
Definition Title.php:1067
Value object representing a user's identity.
Utility class for creating new RC entries.
Interface for objects which can provide a MediaWiki context on request.