MediaWiki 1.41.2
NewPagesPager.php
Go to the documentation of this file.
1<?php
22namespace MediaWiki\Pager;
23
24use ChangesList;
25use ChangeTags;
26use HtmlArmor;
45use RecentChange;
46use stdClass;
47
53
57 protected $opts;
58
60 private $formattedComments = [];
61
62 private GroupPermissionsLookup $groupPermissionsLookup;
63 private HookRunner $hookRunner;
64 private LinkBatchFactory $linkBatchFactory;
65 private NamespaceInfo $namespaceInfo;
66 private ChangeTagsStore $changeTagsStore;
67 private RowCommentFormatter $rowCommentFormatter;
68 private IContentHandlerFactory $contentHandlerFactory;
69
82 public function __construct(
83 IContextSource $context,
84 LinkRenderer $linkRenderer,
85 GroupPermissionsLookup $groupPermissionsLookup,
86 HookContainer $hookContainer,
87 LinkBatchFactory $linkBatchFactory,
88 NamespaceInfo $namespaceInfo,
89 ChangeTagsStore $changeTagsStore,
90 RowCommentFormatter $rowCommentFormatter,
91 IContentHandlerFactory $contentHandlerFactory,
93 ) {
94 parent::__construct( $context, $linkRenderer );
95 $this->groupPermissionsLookup = $groupPermissionsLookup;
96 $this->hookRunner = new HookRunner( $hookContainer );
97 $this->linkBatchFactory = $linkBatchFactory;
98 $this->namespaceInfo = $namespaceInfo;
99 $this->changeTagsStore = $changeTagsStore;
100 $this->rowCommentFormatter = $rowCommentFormatter;
101 $this->contentHandlerFactory = $contentHandlerFactory;
102 $this->opts = $opts;
103 }
104
105 public function getQueryInfo() {
106 $rcQuery = RecentChange::getQueryInfo();
107
108 $conds = [];
109 $conds['rc_new'] = 1;
110
111 $username = $this->opts->getValue( 'username' );
112 $user = Title::makeTitleSafe( NS_USER, $username );
113
114 $size = abs( intval( $this->opts->getValue( 'size' ) ) );
115 if ( $size > 0 ) {
116 if ( $this->opts->getValue( 'size-mode' ) === 'max' ) {
117 $conds[] = 'page_len <= ' . $size;
118 } else {
119 $conds[] = 'page_len >= ' . $size;
120 }
121 }
122
123 if ( $user ) {
124 $conds['actor_name'] = $user->getText();
125 } elseif ( $this->canAnonymousUsersCreatePages() && $this->opts->getValue( 'hideliu' ) ) {
126 # If anons cannot make new pages, don't "exclude logged in users"!
127 $conds['actor_user'] = null;
128 }
129
130 $conds = array_merge( $conds, $this->getNamespaceCond() );
131
132 # If this user cannot see patrolled edits or they are off, don't do dumb queries!
133 if ( $this->opts->getValue( 'hidepatrolled' ) && $this->getUser()->useNPPatrol() ) {
134 $conds['rc_patrolled'] = RecentChange::PRC_UNPATROLLED;
135 }
136
137 if ( $this->opts->getValue( 'hidebots' ) ) {
138 $conds['rc_bot'] = 0;
139 }
140
141 if ( $this->opts->getValue( 'hideredirs' ) ) {
142 $conds['page_is_redirect'] = 0;
143 }
144
145 // Allow changes to the New Pages query
146 $tables = array_merge( $rcQuery['tables'], [ 'page' ] );
147 $fields = array_merge( $rcQuery['fields'], [
148 'length' => 'page_len', 'rev_id' => 'page_latest', 'page_namespace', 'page_title',
149 'page_content_model',
150 ] );
151 $join_conds = [ 'page' => [ 'JOIN', 'page_id=rc_cur_id' ] ] + $rcQuery['joins'];
152
153 $this->hookRunner->onSpecialNewpagesConditions(
154 $this, $this->opts, $conds, $tables, $fields, $join_conds );
155
156 $info = [
157 'tables' => $tables,
158 'fields' => $fields,
159 'conds' => $conds,
160 'options' => [],
161 'join_conds' => $join_conds
162 ];
163
164 // Modify query for tags
165 $this->changeTagsStore->modifyDisplayQuery(
166 $info['tables'],
167 $info['fields'],
168 $info['conds'],
169 $info['join_conds'],
170 $info['options'],
171 $this->opts['tagfilter'],
172 $this->opts['tagInvert']
173 );
174
175 return $info;
176 }
177
178 private function canAnonymousUsersCreatePages() {
179 return $this->groupPermissionsLookup->groupHasPermission( '*', 'createpage' ) ||
180 $this->groupPermissionsLookup->groupHasPermission( '*', 'createtalk' );
181 }
182
183 // Based on ContribsPager.php
184 private function getNamespaceCond() {
185 $namespace = $this->opts->getValue( 'namespace' );
186 if ( $namespace === 'all' || $namespace === '' ) {
187 return [];
188 }
189
190 $namespace = intval( $namespace );
191 if ( $namespace < NS_MAIN ) {
192 // Negative namespaces are invalid
193 return [];
194 }
195
196 $invert = $this->opts->getValue( 'invert' );
197 $associated = $this->opts->getValue( 'associated' );
198
199 $eq_op = $invert ? '!=' : '=';
200 $bool_op = $invert ? 'AND' : 'OR';
201
202 $dbr = $this->getDatabase();
203 $selectedNS = $dbr->addQuotes( $namespace );
204 if ( !$associated ) {
205 return [ "rc_namespace $eq_op $selectedNS" ];
206 }
207
208 $associatedNS = $dbr->addQuotes(
209 $this->namespaceInfo->getAssociated( $namespace )
210 );
211 return [
212 "rc_namespace $eq_op $selectedNS " .
213 $bool_op .
214 " rc_namespace $eq_op $associatedNS"
215 ];
216 }
217
218 public function getIndexField() {
219 return [ [ 'rc_timestamp', 'rc_id' ] ];
220 }
221
222 public function formatRow( $row ) {
223 $title = Title::newFromRow( $row );
224
225 // Revision deletion works on revisions,
226 // so cast our recent change row to a revision row.
227 $revRecord = $this->revisionFromRcResult( $row, $title );
228
229 $classes = [];
230 $attribs = [ 'data-mw-revid' => $row->rev_id ];
231
232 $lang = $this->getLanguage();
233 $dm = $lang->getDirMark();
234
235 $spanTime = Html::element( 'span', [ 'class' => 'mw-newpages-time' ],
236 $lang->userTimeAndDate( $row->rc_timestamp, $this->getUser() )
237 );
238 $linkRenderer = $this->getLinkRenderer();
239 $time = $linkRenderer->makeKnownLink(
240 $title,
241 new HtmlArmor( $spanTime ),
242 [],
243 [ 'oldid' => $row->rc_this_oldid ]
244 );
245
246 $query = $title->isRedirect() ? [ 'redirect' => 'no' ] : [];
247
248 $plink = $linkRenderer->makeKnownLink(
249 $title,
250 null,
251 [ 'class' => 'mw-newpages-pagename' ],
252 $query
253 );
254 $linkArr = [];
255 $linkArr[] = $linkRenderer->makeKnownLink(
256 $title,
257 $this->msg( 'hist' )->text(),
258 [ 'class' => 'mw-newpages-history' ],
259 [ 'action' => 'history' ]
260 );
261 if ( $this->contentHandlerFactory->getContentHandler( $title->getContentModel() )
262 ->supportsDirectEditing()
263 ) {
264 $linkArr[] = $linkRenderer->makeKnownLink(
265 $title,
266 $this->msg( 'editlink' )->text(),
267 [ 'class' => 'mw-newpages-edit' ],
268 [ 'action' => 'edit' ]
269 );
270 }
271 $links = $this->msg( 'parentheses' )->rawParams( $this->getLanguage()
272 ->pipeList( $linkArr ) )->escaped();
273
274 $length = Html::rawElement(
275 'span',
276 [ 'class' => 'mw-newpages-length' ],
277 $this->msg( 'brackets' )->rawParams(
278 $this->msg( 'nbytes' )->numParams( $row->length )->escaped()
279 )->escaped()
280 );
281
282 $ulink = Linker::revUserTools( $revRecord );
283 $rc = RecentChange::newFromRow( $row );
284 if ( ChangesList::userCan( $rc, RevisionRecord::DELETED_COMMENT, $this->getAuthority() ) ) {
285 $comment = $this->formattedComments[$rc->mAttribs['rc_id']];
286 } else {
287 $comment = '<span class="comment">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
288 }
289 if ( ChangesList::isDeleted( $rc, RevisionRecord::DELETED_COMMENT ) ) {
290 $deletedClass = 'history-deleted';
291 if ( ChangesList::isDeleted( $rc, RevisionRecord::DELETED_RESTRICTED ) ) {
292 $deletedClass .= ' mw-history-suppressed';
293 }
294 $comment = '<span class="' . $deletedClass . ' comment">' . $comment . '</span>';
295 }
296
297 if ( $this->getUser()->useNPPatrol() && !$row->rc_patrolled ) {
298 $classes[] = 'not-patrolled';
299 }
300
301 # Add a class for zero byte pages
302 if ( $row->length == 0 ) {
303 $classes[] = 'mw-newpages-zero-byte-page';
304 }
305
306 # Tags, if any.
307 if ( isset( $row->ts_tags ) ) {
308 [ $tagDisplay, $newClasses ] = ChangeTags::formatSummaryRow(
309 $row->ts_tags,
310 'newpages',
311 $this->getContext()
312 );
313 $classes = array_merge( $classes, $newClasses );
314 } else {
315 $tagDisplay = '';
316 }
317
318 # Display the old title if the namespace/title has been changed
319 $oldTitleText = '';
320 $oldTitle = Title::makeTitle( $row->rc_namespace, $row->rc_title );
321
322 if ( !$title->equals( $oldTitle ) ) {
323 $oldTitleText = $oldTitle->getPrefixedText();
324 $oldTitleText = Html::rawElement(
325 'span',
326 [ 'class' => 'mw-newpages-oldtitle' ],
327 $this->msg( 'rc-old-title' )->params( $oldTitleText )->escaped()
328 );
329 }
330
331 $ret = "{$time} {$dm}{$plink} {$links} {$dm}{$length} {$dm}{$ulink} {$comment} "
332 . "{$tagDisplay} {$oldTitleText}";
333
334 // Let extensions add data
335 $this->hookRunner->onNewPagesLineEnding(
336 $this, $ret, $row, $classes, $attribs );
337 $attribs = array_filter( $attribs,
338 [ Sanitizer::class, 'isReservedDataAttribute' ],
339 ARRAY_FILTER_USE_KEY
340 );
341
342 if ( $classes ) {
343 $attribs['class'] = $classes;
344 }
345
346 return Html::rawElement( 'li', $attribs, $ret ) . "\n";
347 }
348
354 protected function revisionFromRcResult( stdClass $result, Title $title ): RevisionRecord {
355 $revRecord = new MutableRevisionRecord( $title );
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', 'rc_namespace', 'rc_title', 'rc_id', true
378 );
379 }
380
381 protected function getStartBody() {
382 return '<ul>';
383 }
384
385 protected function getEndBody() {
386 return '</ul>';
387 }
388}
389
394class_alias( NewPagesPager::class, 'NewPagesPager' );
const NS_USER
Definition Defines.php:66
const NS_MAIN
Definition Defines.php:64
const NS_USER_TALK
Definition Defines.php:67
static formatSummaryRow( $tags, $unused, MessageLocalizer $localizer=null)
Creates HTML for the given tags.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Marks HTML that shouldn't be escaped.
Definition HtmlArmor.php:30
Gateway class for change_tags table.
This is basically a CommentFormatter with a CommentStore dependency, allowing it to retrieve comment ...
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:65
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.
getIndexField()
Returns the name of the index field.
__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.
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:76
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:3162
getContentModel( $flags=0)
Get the page's content model id, see the CONTENT_MODEL_XXX constants.
Definition Title.php:1080
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.