1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 * @ingroup Pager
20 */
21
22use MediaWiki\Cache\LinkBatchFactory;
23use MediaWiki\HookContainer\HookContainer;
24use MediaWiki\HookContainer\HookRunner;
25use MediaWiki\Permissions\PermissionManager;
26use MediaWiki\User\UserFactory;
27use Wikimedia\Rdbms\ILoadBalancer;
28
29/**
30 * @ingroup Pager
31 */
32class NewPagesPager extends ReverseChronologicalPager {
33
34	/**
35	 * @var FormOptions
36	 */
37	protected $opts;
38
39	/**
40	 * @var SpecialNewpages
41	 */
42	protected $mForm;
43
44	/** @var LinkBatchFactory */
45	private $linkBatchFactory;
46
47	/** @var HookRunner */
48	private $hookRunner;
49
50	/** @var PermissionManager */
51	private $permissionManager;
52
53	/** @var NamespaceInfo */
54	private $namespaceInfo;
55
56	/** @var ActorMigration */
57	private $actorMigration;
58
59	/** @var UserFactory */
60	private $userFactory;
61
62	/**
63	 * @param SpecialNewpages $form
64	 * @param FormOptions $opts
65	 * @param LinkBatchFactory $linkBatchFactory
66	 * @param HookContainer $hookContainer
67	 * @param PermissionManager $permissionManager
68	 * @param ILoadBalancer $loadBalancer
69	 * @param NamespaceInfo $namespaceInfo
70	 * @param ActorMigration $actorMigration
71	 * @param UserFactory $userFactory
72	 */
73	public function __construct(
74		$form,
75		FormOptions $opts,
76		LinkBatchFactory $linkBatchFactory,
77		HookContainer $hookContainer,
78		PermissionManager $permissionManager,
79		ILoadBalancer $loadBalancer,
80		NamespaceInfo $namespaceInfo,
81		ActorMigration $actorMigration,
82		UserFactory $userFactory
83	) {
84		// Set database before parent constructor to avoid setting it there with wfGetDB
85		$this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
86		parent::__construct( $form->getContext() );
87		$this->mForm = $form;
88		$this->opts = $opts;
89		$this->linkBatchFactory = $linkBatchFactory;
90		$this->hookRunner = new HookRunner( $hookContainer );
91		$this->permissionManager = $permissionManager;
92		$this->namespaceInfo = $namespaceInfo;
93		$this->actorMigration = $actorMigration;
94		$this->userFactory = $userFactory;
95	}
96
97	public function getQueryInfo() {
98		$rcQuery = RecentChange::getQueryInfo();
99
100		$conds = [];
101		$conds['rc_new'] = 1;
102
103		$username = $this->opts->getValue( 'username' );
104		$user = Title::makeTitleSafe( NS_USER, $username );
105
106		$size = abs( intval( $this->opts->getValue( 'size' ) ) );
107		if ( $size > 0 ) {
108			if ( $this->opts->getValue( 'size-mode' ) === 'max' ) {
109				$conds[] = 'page_len <= ' . $size;
110			} else {
111				$conds[] = 'page_len >= ' . $size;
112			}
113		}
114
115		if ( $user ) {
116			$userObj = $this->userFactory->newFromName( $user->getText(), UserFactory::RIGOR_NONE );
117			$conds[] = $this->actorMigration->getWhere(
118				$this->getDatabase(), 'rc_user', $userObj, false
119			)['conds'];
120		} elseif ( $this->canAnonymousUsersCreatePages() && $this->opts->getValue( 'hideliu' ) ) {
121			# If anons cannot make new pages, don't "exclude logged in users"!
122			$conds[] = $this->actorMigration->isAnon( $rcQuery['fields']['rc_user'] );
123		}
124
125		$conds = array_merge( $conds, $this->getNamespaceCond() );
126
127		# If this user cannot see patrolled edits or they are off, don't do dumb queries!
128		if ( $this->opts->getValue( 'hidepatrolled' ) && $this->getUser()->useNPPatrol() ) {
129			$conds['rc_patrolled'] = RecentChange::PRC_UNPATROLLED;
130		}
131
132		if ( $this->opts->getValue( 'hidebots' ) ) {
133			$conds['rc_bot'] = 0;
134		}
135
136		if ( $this->opts->getValue( 'hideredirs' ) ) {
137			$conds['page_is_redirect'] = 0;
138		}
139
140		// Allow changes to the New Pages query
141		$tables = array_merge( $rcQuery['tables'], [ 'page' ] );
142		$fields = array_merge( $rcQuery['fields'], [
143			'length' => 'page_len', 'rev_id' => 'page_latest', 'page_namespace', 'page_title',
144			'page_content_model',
145		] );
146		$join_conds = [ 'page' => [ 'JOIN', 'page_id=rc_cur_id' ] ] + $rcQuery['joins'];
147
148		$this->hookRunner->onSpecialNewpagesConditions(
149			$this, $this->opts, $conds, $tables, $fields, $join_conds );
150
151		$info = [
152			'tables' => $tables,
153			'fields' => $fields,
154			'conds' => $conds,
155			'options' => [],
156			'join_conds' => $join_conds
157		];
158
159		// Modify query for tags
160		ChangeTags::modifyDisplayQuery(
161			$info['tables'],
162			$info['fields'],
163			$info['conds'],
164			$info['join_conds'],
165			$info['options'],
166			$this->opts['tagfilter']
167		);
168
169		return $info;
170	}
171
172	private function canAnonymousUsersCreatePages() {
173		return $this->permissionManager->groupHasPermission( '*', 'createpage' ) ||
174			$this->permissionManager->groupHasPermission( '*', 'createtalk' );
175	}
176
177	// Based on ContribsPager.php
178	private function getNamespaceCond() {
179		$namespace = $this->opts->getValue( 'namespace' );
180		if ( $namespace === 'all' || $namespace === '' ) {
181			return [];
182		}
183
184		$namespace = intval( $namespace );
185		if ( $namespace < NS_MAIN ) {
186			// Negative namespaces are invalid
187			return [];
188		}
189
190		$invert = $this->opts->getValue( 'invert' );
191		$associated = $this->opts->getValue( 'associated' );
192
193		$eq_op = $invert ? '!=' : '=';
194		$bool_op = $invert ? 'AND' : 'OR';
195
196		$dbr = $this->getDatabase();
197		$selectedNS = $dbr->addQuotes( $namespace );
198		if ( !$associated ) {
199			return [ "rc_namespace $eq_op $selectedNS" ];
200		}
201
202		$associatedNS = $dbr->addQuotes(
203			$this->namespaceInfo->getAssociated( $namespace )
204		);
205		return [
206			"rc_namespace $eq_op $selectedNS " .
207			$bool_op .
208			" rc_namespace $eq_op $associatedNS"
209		];
210	}
211
212	public function getIndexField() {
213		return 'rc_timestamp';
214	}
215
216	public function formatRow( $row ) {
217		return $this->mForm->formatRow( $row );
218	}
219
220	protected function getStartBody() {
221		# Do a batch existence check on pages
222		$linkBatch = $this->linkBatchFactory->newLinkBatch();
223		foreach ( $this->mResult as $row ) {
224			$linkBatch->add( NS_USER, $row->rc_user_text );
225			$linkBatch->add( NS_USER_TALK, $row->rc_user_text );
226			$linkBatch->add( $row->page_namespace, $row->page_title );
227		}
228		$linkBatch->execute();
229
230		return '<ul>';
231	}
232
233	protected function getEndBody() {
234		return '</ul>';
235	}
236}
237