DeletedContributionsPage::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Implements Special:DeletedContributions
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 * @ingroup SpecialPage
22
 */
23
24
/**
25
 * Implements Special:DeletedContributions to display archived revisions
26
 * @ingroup SpecialPage
27
 */
28
class DeletedContributionsPage extends SpecialPage {
29
	function __construct() {
30
		parent::__construct( 'DeletedContributions', 'deletedhistory',
31
			/*listed*/true, /*function*/false, /*file*/false );
32
	}
33
34
	/**
35
	 * Special page "deleted user contributions".
36
	 * Shows a list of the deleted contributions of a user.
37
	 *
38
	 * @param string $par (optional) user name of the user for which to show the contributions
39
	 */
40
	function execute( $par ) {
41
		$this->setHeaders();
42
		$this->outputHeader();
43
44
		$user = $this->getUser();
45
46
		if ( !$this->userCanExecute( $user ) ) {
47
			$this->displayRestrictionError();
48
49
			return;
50
		}
51
52
		$request = $this->getRequest();
53
		$out = $this->getOutput();
54
		$out->setPageTitle( $this->msg( 'deletedcontributions-title' ) );
55
56
		$options = [];
57
58
		if ( $par !== null ) {
59
			$target = $par;
60
		} else {
61
			$target = $request->getVal( 'target' );
62
		}
63
64
		if ( !strlen( $target ) ) {
65
			$out->addHTML( $this->getForm( '' ) );
66
67
			return;
68
		}
69
70
		$options['limit'] = $request->getInt( 'limit',
71
			$this->getConfig()->get( 'QueryPageDefaultLimit' ) );
72
		$options['target'] = $target;
73
74
		$userObj = User::newFromName( $target, false );
75
		if ( !$userObj ) {
76
			$out->addHTML( $this->getForm( '' ) );
77
78
			return;
79
		}
80
		$this->getSkin()->setRelevantUser( $userObj );
81
82
		$target = $userObj->getName();
83
		$out->addSubtitle( $this->getSubTitle( $userObj ) );
84
85
		$ns = $request->getVal( 'namespace', null );
86
		if ( $ns !== null && $ns !== '' ) {
87
			$options['namespace'] = intval( $ns );
88
		} else {
89
			$options['namespace'] = '';
90
		}
91
92
		$out->addHTML( $this->getForm( $options ) );
93
94
		$pager = new DeletedContribsPager( $this->getContext(), $target, $options['namespace'] );
95
		if ( !$pager->getNumRows() ) {
96
			$out->addWikiMsg( 'nocontribs' );
97
98
			return;
99
		}
100
101
		# Show a message about replica DB lag, if applicable
102
		$lag = wfGetLB()->safeGetLag( $pager->getDatabase() );
0 ignored issues
show
Deprecated Code introduced by
The function wfGetLB() has been deprecated with message: since 1.27, use MediaWikiServices::getDBLoadBalancer() or MediaWikiServices::getDBLoadBalancerFactory() instead.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
Bug introduced by
It seems like $pager->getDatabase() can be null; however, safeGetLag() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
103
		if ( $lag > 0 ) {
104
			$out->showLagWarning( $lag );
0 ignored issues
show
Bug introduced by
It seems like $lag defined by wfGetLB()->safeGetLag($pager->getDatabase()) on line 102 can also be of type boolean; however, OutputPage::showLagWarning() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
105
		}
106
107
		$out->addHTML(
108
			'<p>' . $pager->getNavigationBar() . '</p>' .
109
				$pager->getBody() .
110
				'<p>' . $pager->getNavigationBar() . '</p>' );
111
112
		# If there were contributions, and it was a valid user or IP, show
113
		# the appropriate "footer" message - WHOIS tools, etc.
114
		if ( $target != 'newbies' ) {
115
			$message = IP::isIPAddress( $target ) ?
116
				'sp-contributions-footer-anon' :
117
				'sp-contributions-footer';
118
119
			if ( !$this->msg( $message )->isDisabled() ) {
120
				$out->wrapWikiMsg(
121
					"<div class='mw-contributions-footer'>\n$1\n</div>",
122
					[ $message, $target ]
123
				);
124
			}
125
		}
126
	}
127
128
	/**
129
	 * Generates the subheading with links
130
	 * @param User $userObj User object for the target
131
	 * @return string Appropriately-escaped HTML to be output literally
132
	 */
133
	function getSubTitle( $userObj ) {
134
		$linkRenderer = $this->getLinkRenderer();
135
		if ( $userObj->isAnon() ) {
136
			$user = htmlspecialchars( $userObj->getName() );
137
		} else {
138
			$user = $linkRenderer->makeLink( $userObj->getUserPage(), $userObj->getName() );
139
		}
140
		$links = '';
141
		$nt = $userObj->getUserPage();
142
		$talk = $nt->getTalkPage();
143
		if ( $talk ) {
144
			$tools = SpecialContributions::getUserLinks( $this, $userObj );
145
146
			# Link to contributions
147
			$insert['contribs'] = $linkRenderer->makeKnownLink(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$insert was never initialized. Although not strictly required by PHP, it is generally a good practice to add $insert = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
148
				SpecialPage::getTitleFor( 'Contributions', $nt->getDBkey() ),
149
				$this->msg( 'sp-deletedcontributions-contribs' )->text()
150
			);
151
152
			// Swap out the deletedcontribs link for our contribs one
153
			$tools = wfArrayInsertAfter( $tools, $insert, 'deletedcontribs' );
154
			unset( $tools['deletedcontribs'] );
155
156
			$links = $this->getLanguage()->pipeList( $tools );
157
158
			// Show a note if the user is blocked and display the last block log entry.
159
			$block = Block::newFromTarget( $userObj, $userObj );
160 View Code Duplication
			if ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
161
				if ( $block->getType() == Block::TYPE_RANGE ) {
162
					$nt = MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget();
163
				}
164
165
				// LogEventsList::showLogExtract() wants the first parameter by ref
166
				$out = $this->getOutput();
167
				LogEventsList::showLogExtract(
168
					$out,
169
					'block',
170
					$nt,
171
					'',
172
					[
173
						'lim' => 1,
174
						'showIfEmpty' => false,
175
						'msgKey' => [
176
							'sp-contributions-blocked-notice',
177
							$userObj->getName() # Support GENDER in 'sp-contributions-blocked-notice'
178
						],
179
						'offset' => '' # don't use $this->getRequest() parameter offset
180
					]
181
				);
182
			}
183
		}
184
185
		return $this->msg( 'contribsub2' )->rawParams( $user, $links )->params( $userObj->getName() );
186
	}
187
188
	/**
189
	 * Generates the namespace selector form with hidden attributes.
190
	 * @param array $options The options to be included.
191
	 * @return string
192
	 */
193
	function getForm( $options ) {
194
		$options['title'] = $this->getPageTitle()->getPrefixedText();
195
		if ( !isset( $options['target'] ) ) {
196
			$options['target'] = '';
197
		} else {
198
			$options['target'] = str_replace( '_', ' ', $options['target'] );
199
		}
200
201
		if ( !isset( $options['namespace'] ) ) {
202
			$options['namespace'] = '';
203
		}
204
205
		if ( !isset( $options['contribs'] ) ) {
206
			$options['contribs'] = 'user';
207
		}
208
209
		if ( $options['contribs'] == 'newbie' ) {
210
			$options['target'] = '';
211
		}
212
213
		$f = Xml::openElement( 'form', [ 'method' => 'get', 'action' => wfScript() ] );
214
215
		foreach ( $options as $name => $value ) {
216
			if ( in_array( $name, [ 'namespace', 'target', 'contribs' ] ) ) {
217
				continue;
218
			}
219
			$f .= "\t" . Html::hidden( $name, $value ) . "\n";
220
		}
221
222
		$this->getOutput()->addModules( 'mediawiki.userSuggest' );
223
224
		$f .= Xml::openElement( 'fieldset' );
225
		$f .= Xml::element( 'legend', [], $this->msg( 'sp-contributions-search' )->text() );
226
		$f .= Xml::tags(
227
			'label',
228
			[ 'for' => 'target' ],
229
			$this->msg( 'sp-contributions-username' )->parse()
230
		) . ' ';
231
		$f .= Html::input(
232
			'target',
233
			$options['target'],
234
			'text',
235
			[
236
				'size' => '20',
237
				'required' => '',
238
				'class' => [
239
					'mw-autocomplete-user', // used by mediawiki.userSuggest
240
				],
241
			] + ( $options['target'] ? [] : [ 'autofocus' ] )
242
		) . ' ';
243
		$f .= Html::namespaceSelector(
244
			[
245
				'selected' => $options['namespace'],
246
				'all' => '',
247
				'label' => $this->msg( 'namespace' )->text()
248
			],
249
			[
250
				'name' => 'namespace',
251
				'id' => 'namespace',
252
				'class' => 'namespaceselector',
253
			]
254
		) . ' ';
255
		$f .= Xml::submitButton( $this->msg( 'sp-contributions-submit' )->text() );
256
		$f .= Xml::closeElement( 'fieldset' );
257
		$f .= Xml::closeElement( 'form' );
258
259
		return $f;
260
	}
261
262
	/**
263
	 * Return an array of subpages beginning with $search that this special page will accept.
264
	 *
265
	 * @param string $search Prefix to search for
266
	 * @param int $limit Maximum number of results to return (usually 10)
267
	 * @param int $offset Number of results to skip (usually 0)
268
	 * @return string[] Matching subpages
269
	 */
270 View Code Duplication
	public function prefixSearchSubpages( $search, $limit, $offset ) {
271
		$user = User::newFromName( $search );
272
		if ( !$user ) {
273
			// No prefix suggestion for invalid user
274
			return [];
275
		}
276
		// Autocomplete subpage as user list - public to allow caching
277
		return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
278
	}
279
280
	protected function getGroupName() {
281
		return 'users';
282
	}
283
}
284