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\GroupPermissionsLookup;
26use Wikimedia\Rdbms\ILoadBalancer;
27
28/**
29 * @ingroup Pager
30 */
31class NewPagesPager extends ReverseChronologicalPager {
32
33	/**
34	 * @var FormOptions
35	 */
36	protected $opts;
37
38	/**
39	 * @var SpecialNewpages
40	 */
41	protected $mForm;
42
43	/** @var LinkBatchFactory */
44	private $linkBatchFactory;
45
46	/** @var HookRunner */
47	private $hookRunner;
48
49	/** @var GroupPermissionsLookup */
50	private $groupPermissionsLookup;
51
52	/** @var NamespaceInfo */
53	private $namespaceInfo;
54
55	/**
56	 * @param SpecialNewpages $form
57	 * @param FormOptions $opts
58	 * @param LinkBatchFactory $linkBatchFactory
59	 * @param HookContainer $hookContainer
60	 * @param GroupPermissionsLookup $groupPermissionsLookup
61	 * @param ILoadBalancer $loadBalancer
62	 * @param NamespaceInfo $namespaceInfo
63	 */
64	public function __construct(
65		$form,
66		FormOptions $opts,
67		LinkBatchFactory $linkBatchFactory,
68		HookContainer $hookContainer,
69		GroupPermissionsLookup $groupPermissionsLookup,
70		ILoadBalancer $loadBalancer,
71		NamespaceInfo $namespaceInfo
72	) {
73		// Set database before parent constructor to avoid setting it there with wfGetDB
74		$this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
75		parent::__construct( $form->getContext() );
76		$this->mForm = $form;
77		$this->opts = $opts;
78		$this->linkBatchFactory = $linkBatchFactory;
79		$this->hookRunner = new HookRunner( $hookContainer );
80		$this->groupPermissionsLookup = $groupPermissionsLookup;
81		$this->namespaceInfo = $namespaceInfo;
82	}
83
84	public function getQueryInfo() {
85		$rcQuery = RecentChange::getQueryInfo();
86
87		$conds = [];
88		$conds['rc_new'] = 1;
89
90		$username = $this->opts->getValue( 'username' );
91		$user = Title::makeTitleSafe( NS_USER, $username );
92
93		$size = abs( intval( $this->opts->getValue( 'size' ) ) );
94		if ( $size > 0 ) {
95			if ( $this->opts->getValue( 'size-mode' ) === 'max' ) {
96				$conds[] = 'page_len <= ' . $size;
97			} else {
98				$conds[] = 'page_len >= ' . $size;
99			}
100		}
101
102		if ( $user ) {
103			$conds['actor_name'] = $user->getText();
104		} elseif ( $this->canAnonymousUsersCreatePages() && $this->opts->getValue( 'hideliu' ) ) {
105			# If anons cannot make new pages, don't "exclude logged in users"!
106			$conds['actor_user'] = null;
107		}
108
109		$conds = array_merge( $conds, $this->getNamespaceCond() );
110
111		# If this user cannot see patrolled edits or they are off, don't do dumb queries!
112		if ( $this->opts->getValue( 'hidepatrolled' ) && $this->getUser()->useNPPatrol() ) {
113			$conds['rc_patrolled'] = RecentChange::PRC_UNPATROLLED;
114		}
115
116		if ( $this->opts->getValue( 'hidebots' ) ) {
117			$conds['rc_bot'] = 0;
118		}
119
120		if ( $this->opts->getValue( 'hideredirs' ) ) {
121			$conds['page_is_redirect'] = 0;
122		}
123
124		// Allow changes to the New Pages query
125		$tables = array_merge( $rcQuery['tables'], [ 'page' ] );
126		$fields = array_merge( $rcQuery['fields'], [
127			'length' => 'page_len', 'rev_id' => 'page_latest', 'page_namespace', 'page_title',
128			'page_content_model',
129		] );
130		$join_conds = [ 'page' => [ 'JOIN', 'page_id=rc_cur_id' ] ] + $rcQuery['joins'];
131
132		$this->hookRunner->onSpecialNewpagesConditions(
133			$this, $this->opts, $conds, $tables, $fields, $join_conds );
134
135		$info = [
136			'tables' => $tables,
137			'fields' => $fields,
138			'conds' => $conds,
139			'options' => [],
140			'join_conds' => $join_conds
141		];
142
143		// Modify query for tags
144		ChangeTags::modifyDisplayQuery(
145			$info['tables'],
146			$info['fields'],
147			$info['conds'],
148			$info['join_conds'],
149			$info['options'],
150			$this->opts['tagfilter']
151		);
152
153		return $info;
154	}
155
156	private function canAnonymousUsersCreatePages() {
157		return $this->groupPermissionsLookup->groupHasPermission( '*', 'createpage' ) ||
158			$this->groupPermissionsLookup->groupHasPermission( '*', 'createtalk' );
159	}
160
161	// Based on ContribsPager.php
162	private function getNamespaceCond() {
163		$namespace = $this->opts->getValue( 'namespace' );
164		if ( $namespace === 'all' || $namespace === '' ) {
165			return [];
166		}
167
168		$namespace = intval( $namespace );
169		if ( $namespace < NS_MAIN ) {
170			// Negative namespaces are invalid
171			return [];
172		}
173
174		$invert = $this->opts->getValue( 'invert' );
175		$associated = $this->opts->getValue( 'associated' );
176
177		$eq_op = $invert ? '!=' : '=';
178		$bool_op = $invert ? 'AND' : 'OR';
179
180		$dbr = $this->getDatabase();
181		$selectedNS = $dbr->addQuotes( $namespace );
182		if ( !$associated ) {
183			return [ "rc_namespace $eq_op $selectedNS" ];
184		}
185
186		$associatedNS = $dbr->addQuotes(
187			$this->namespaceInfo->getAssociated( $namespace )
188		);
189		return [
190			"rc_namespace $eq_op $selectedNS " .
191			$bool_op .
192			" rc_namespace $eq_op $associatedNS"
193		];
194	}
195
196	public function getIndexField() {
197		return 'rc_timestamp';
198	}
199
200	public function formatRow( $row ) {
201		return $this->mForm->formatRow( $row );
202	}
203
204	protected function getStartBody() {
205		# Do a batch existence check on pages
206		$linkBatch = $this->linkBatchFactory->newLinkBatch();
207		foreach ( $this->mResult as $row ) {
208			$linkBatch->add( NS_USER, $row->rc_user_text );
209			$linkBatch->add( NS_USER_TALK, $row->rc_user_text );
210			$linkBatch->add( $row->page_namespace, $row->page_title );
211		}
212		$linkBatch->execute();
213
214		return '<ul>';
215	}
216
217	protected function getEndBody() {
218		return '</ul>';
219	}
220}
221