Completed
Branch master (54277f)
by
unknown
24:54
created

Article::replaceSection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 8
rs 9.4285
cc 1
eloc 5
nc 1
nop 4
1
<?php
2
/**
3
 * User interface for page actions.
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
 */
22
23
/**
24
 * Class for viewing MediaWiki article and history.
25
 *
26
 * This maintains WikiPage functions for backwards compatibility.
27
 *
28
 * @todo Move and rewrite code to an Action class
29
 *
30
 * See design.txt for an overview.
31
 * Note: edit user interface and cache support functions have been
32
 * moved to separate EditPage and HTMLFileCache classes.
33
 */
34
class Article implements Page {
35
	/** @var IContextSource The context this Article is executed in */
36
	protected $mContext;
37
38
	/** @var WikiPage The WikiPage object of this instance */
39
	protected $mPage;
40
41
	/** @var ParserOptions ParserOptions object for $wgUser articles */
42
	public $mParserOptions;
43
44
	/**
45
	 * @var string Text of the revision we are working on
46
	 * @todo BC cruft
47
	 */
48
	public $mContent;
49
50
	/**
51
	 * @var Content Content of the revision we are working on
52
	 * @since 1.21
53
	 */
54
	public $mContentObject;
55
56
	/** @var bool Is the content ($mContent) already loaded? */
57
	public $mContentLoaded = false;
58
59
	/** @var int|null The oldid of the article that is to be shown, 0 for the current revision */
60
	public $mOldId;
61
62
	/** @var Title Title from which we were redirected here */
63
	public $mRedirectedFrom = null;
64
65
	/** @var string|bool URL to redirect to or false if none */
66
	public $mRedirectUrl = false;
67
68
	/** @var int Revision ID of revision we are working on */
69
	public $mRevIdFetched = 0;
70
71
	/** @var Revision Revision we are working on */
72
	public $mRevision = null;
73
74
	/** @var ParserOutput */
75
	public $mParserOutput;
76
77
	/**
78
	 * Constructor and clear the article
79
	 * @param Title $title Reference to a Title object.
80
	 * @param int $oldId Revision ID, null to fetch from request, zero for current
81
	 */
82
	public function __construct( Title $title, $oldId = null ) {
83
		$this->mOldId = $oldId;
84
		$this->mPage = $this->newPage( $title );
85
	}
86
87
	/**
88
	 * @param Title $title
89
	 * @return WikiPage
90
	 */
91
	protected function newPage( Title $title ) {
92
		return new WikiPage( $title );
93
	}
94
95
	/**
96
	 * Constructor from a page id
97
	 * @param int $id Article ID to load
98
	 * @return Article|null
99
	 */
100
	public static function newFromID( $id ) {
101
		$t = Title::newFromID( $id );
102
		# @todo FIXME: Doesn't inherit right
103
		return $t == null ? null : new self( $t );
104
		# return $t == null ? null : new static( $t ); // PHP 5.3
105
	}
106
107
	/**
108
	 * Create an Article object of the appropriate class for the given page.
109
	 *
110
	 * @param Title $title
111
	 * @param IContextSource $context
112
	 * @return Article
113
	 */
114
	public static function newFromTitle( $title, IContextSource $context ) {
115
		if ( NS_MEDIA == $title->getNamespace() ) {
116
			// FIXME: where should this go?
117
			$title = Title::makeTitle( NS_FILE, $title->getDBkey() );
118
		}
119
120
		$page = null;
121
		Hooks::run( 'ArticleFromTitle', [ &$title, &$page, $context ] );
122
		if ( !$page ) {
123
			switch ( $title->getNamespace() ) {
124
				case NS_FILE:
125
					$page = new ImagePage( $title );
126
					break;
127
				case NS_CATEGORY:
128
					$page = new CategoryPage( $title );
129
					break;
130
				default:
131
					$page = new Article( $title );
132
			}
133
		}
134
		$page->setContext( $context );
135
136
		return $page;
137
	}
138
139
	/**
140
	 * Create an Article object of the appropriate class for the given page.
141
	 *
142
	 * @param WikiPage $page
143
	 * @param IContextSource $context
144
	 * @return Article
145
	 */
146
	public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
147
		$article = self::newFromTitle( $page->getTitle(), $context );
148
		$article->mPage = $page; // override to keep process cached vars
149
		return $article;
150
	}
151
152
	/**
153
	 * Tell the page view functions that this view was redirected
154
	 * from another page on the wiki.
155
	 * @param Title $from
156
	 */
157
	public function setRedirectedFrom( Title $from ) {
158
		$this->mRedirectedFrom = $from;
159
	}
160
161
	/**
162
	 * Get the title object of the article
163
	 *
164
	 * @return Title Title object of this page
165
	 */
166
	public function getTitle() {
167
		return $this->mPage->getTitle();
168
	}
169
170
	/**
171
	 * Get the WikiPage object of this instance
172
	 *
173
	 * @since 1.19
174
	 * @return WikiPage
175
	 */
176
	public function getPage() {
177
		return $this->mPage;
178
	}
179
180
	/**
181
	 * Clear the object
182
	 */
183
	public function clear() {
184
		$this->mContentLoaded = false;
185
186
		$this->mRedirectedFrom = null; # Title object if set
187
		$this->mRevIdFetched = 0;
188
		$this->mRedirectUrl = false;
189
190
		$this->mPage->clear();
191
	}
192
193
	/**
194
	 * Note that getContent does not follow redirects anymore.
195
	 * If you need to fetch redirectable content easily, try
196
	 * the shortcut in WikiPage::getRedirectTarget()
197
	 *
198
	 * This function has side effects! Do not use this function if you
199
	 * only want the real revision text if any.
200
	 *
201
	 * @deprecated since 1.21; use WikiPage::getContent() instead
202
	 *
203
	 * @return string Return the text of this revision
204
	 */
205
	public function getContent() {
206
		ContentHandler::deprecated( __METHOD__, '1.21' );
207
		$content = $this->getContentObject();
208
		return ContentHandler::getContentText( $content );
209
	}
210
211
	/**
212
	 * Returns a Content object representing the pages effective display content,
213
	 * not necessarily the revision's content!
214
	 *
215
	 * Note that getContent does not follow redirects anymore.
216
	 * If you need to fetch redirectable content easily, try
217
	 * the shortcut in WikiPage::getRedirectTarget()
218
	 *
219
	 * This function has side effects! Do not use this function if you
220
	 * only want the real revision text if any.
221
	 *
222
	 * @return Content Return the content of this revision
223
	 *
224
	 * @since 1.21
225
	 */
226
	protected function getContentObject() {
227
228
		if ( $this->mPage->getId() === 0 ) {
229
			# If this is a MediaWiki:x message, then load the messages
230
			# and return the message value for x.
231
			if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
232
				$text = $this->getTitle()->getDefaultMessageText();
233
				if ( $text === false ) {
234
					$text = '';
235
				}
236
237
				$content = ContentHandler::makeContent( $text, $this->getTitle() );
238
			} else {
239
				$message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
240
				$content = new MessageContent( $message, null, 'parsemag' );
241
			}
242
		} else {
243
			$this->fetchContentObject();
244
			$content = $this->mContentObject;
245
		}
246
247
		return $content;
248
	}
249
250
	/**
251
	 * @return int The oldid of the article that is to be shown, 0 for the current revision
252
	 */
253
	public function getOldID() {
254
		if ( is_null( $this->mOldId ) ) {
255
			$this->mOldId = $this->getOldIDFromRequest();
256
		}
257
258
		return $this->mOldId;
259
	}
260
261
	/**
262
	 * Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect
263
	 *
264
	 * @return int The old id for the request
265
	 */
266
	public function getOldIDFromRequest() {
267
		$this->mRedirectUrl = false;
268
269
		$request = $this->getContext()->getRequest();
270
		$oldid = $request->getIntOrNull( 'oldid' );
271
272
		if ( $oldid === null ) {
273
			return 0;
274
		}
275
276
		if ( $oldid !== 0 ) {
277
			# Load the given revision and check whether the page is another one.
278
			# In that case, update this instance to reflect the change.
279
			if ( $oldid === $this->mPage->getLatest() ) {
280
				$this->mRevision = $this->mPage->getRevision();
281
			} else {
282
				$this->mRevision = Revision::newFromId( $oldid );
283
				if ( $this->mRevision !== null ) {
284
					// Revision title doesn't match the page title given?
285
					if ( $this->mPage->getId() != $this->mRevision->getPage() ) {
286
						$function = [ get_class( $this->mPage ), 'newFromID' ];
287
						$this->mPage = call_user_func( $function, $this->mRevision->getPage() );
288
					}
289
				}
290
			}
291
		}
292
293
		if ( $request->getVal( 'direction' ) == 'next' ) {
294
			$nextid = $this->getTitle()->getNextRevisionID( $oldid );
295
			if ( $nextid ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nextid of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
296
				$oldid = $nextid;
297
				$this->mRevision = null;
298
			} else {
299
				$this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
300
			}
301
		} elseif ( $request->getVal( 'direction' ) == 'prev' ) {
302
			$previd = $this->getTitle()->getPreviousRevisionID( $oldid );
303
			if ( $previd ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $previd of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
304
				$oldid = $previd;
305
				$this->mRevision = null;
306
			}
307
		}
308
309
		return $oldid;
310
	}
311
312
	/**
313
	 * Get text of an article from database
314
	 * Does *NOT* follow redirects.
315
	 *
316
	 * @protected
317
	 * @note This is really internal functionality that should really NOT be
318
	 * used by other functions. For accessing article content, use the WikiPage
319
	 * class, especially WikiBase::getContent(). However, a lot of legacy code
320
	 * uses this method to retrieve page text from the database, so the function
321
	 * has to remain public for now.
322
	 *
323
	 * @return string|bool String containing article contents, or false if null
324
	 * @deprecated since 1.21, use WikiPage::getContent() instead
325
	 */
326
	function fetchContent() {
327
		// BC cruft!
328
329
		ContentHandler::deprecated( __METHOD__, '1.21' );
330
331
		if ( $this->mContentLoaded && $this->mContent ) {
332
			return $this->mContent;
333
		}
334
335
		$content = $this->fetchContentObject();
336
337
		if ( !$content ) {
338
			return false;
339
		}
340
341
		// @todo Get rid of mContent everywhere!
342
		$this->mContent = ContentHandler::getContentText( $content );
343
		ContentHandler::runLegacyHooks( 'ArticleAfterFetchContent', [ &$this, &$this->mContent ] );
344
345
		return $this->mContent;
346
	}
347
348
	/**
349
	 * Get text content object
350
	 * Does *NOT* follow redirects.
351
	 * @todo When is this null?
352
	 *
353
	 * @note Code that wants to retrieve page content from the database should
354
	 * use WikiPage::getContent().
355
	 *
356
	 * @return Content|null|bool
357
	 *
358
	 * @since 1.21
359
	 */
360
	protected function fetchContentObject() {
361
		if ( $this->mContentLoaded ) {
362
			return $this->mContentObject;
363
		}
364
365
		$this->mContentLoaded = true;
366
		$this->mContent = null;
367
368
		$oldid = $this->getOldID();
369
370
		# Pre-fill content with error message so that if something
371
		# fails we'll have something telling us what we intended.
372
		// XXX: this isn't page content but a UI message. horrible.
373
		$this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
374
375
		if ( $oldid ) {
376
			# $this->mRevision might already be fetched by getOldIDFromRequest()
377
			if ( !$this->mRevision ) {
378
				$this->mRevision = Revision::newFromId( $oldid );
379
				if ( !$this->mRevision ) {
380
					wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
381
					return false;
382
				}
383
			}
384
		} else {
385
			$oldid = $this->mPage->getLatest();
386
			if ( !$oldid ) {
387
				wfDebug( __METHOD__ . " failed to find page data for title " .
388
					$this->getTitle()->getPrefixedText() . "\n" );
389
				return false;
390
			}
391
392
			# Update error message with correct oldid
393
			$this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
394
395
			$this->mRevision = $this->mPage->getRevision();
396
397
			if ( !$this->mRevision ) {
398
				wfDebug( __METHOD__ . " failed to retrieve current page, rev_id $oldid\n" );
399
				return false;
400
			}
401
		}
402
403
		// @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
404
		// We should instead work with the Revision object when we need it...
405
		// Loads if user is allowed
406
		$content = $this->mRevision->getContent(
407
			Revision::FOR_THIS_USER,
408
			$this->getContext()->getUser()
409
		);
410
411
		if ( !$content ) {
412
			wfDebug( __METHOD__ . " failed to retrieve content of revision " .
413
				$this->mRevision->getId() . "\n" );
414
			return false;
415
		}
416
417
		$this->mContentObject = $content;
418
		$this->mRevIdFetched = $this->mRevision->getId();
419
420
		Hooks::run( 'ArticleAfterFetchContentObject', [ &$this, &$this->mContentObject ] );
421
422
		return $this->mContentObject;
423
	}
424
425
	/**
426
	 * Returns true if the currently-referenced revision is the current edit
427
	 * to this page (and it exists).
428
	 * @return bool
429
	 */
430
	public function isCurrent() {
431
		# If no oldid, this is the current version.
432
		if ( $this->getOldID() == 0 ) {
433
			return true;
434
		}
435
436
		return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
437
	}
438
439
	/**
440
	 * Get the fetched Revision object depending on request parameters or null
441
	 * on failure.
442
	 *
443
	 * @since 1.19
444
	 * @return Revision|null
445
	 */
446
	public function getRevisionFetched() {
447
		$this->fetchContentObject();
448
449
		return $this->mRevision;
450
	}
451
452
	/**
453
	 * Use this to fetch the rev ID used on page views
454
	 *
455
	 * @return int Revision ID of last article revision
456
	 */
457
	public function getRevIdFetched() {
458
		if ( $this->mRevIdFetched ) {
459
			return $this->mRevIdFetched;
460
		} else {
461
			return $this->mPage->getLatest();
462
		}
463
	}
464
465
	/**
466
	 * This is the default action of the index.php entry point: just view the
467
	 * page of the given title.
468
	 */
469
	public function view() {
470
		global $wgUseFileCache, $wgUseETag, $wgDebugToolbar, $wgMaxRedirects;
471
472
		# Get variables from query string
473
		# As side effect this will load the revision and update the title
474
		# in a revision ID is passed in the request, so this should remain
475
		# the first call of this method even if $oldid is used way below.
476
		$oldid = $this->getOldID();
477
478
		$user = $this->getContext()->getUser();
479
		# Another whitelist check in case getOldID() is altering the title
480
		$permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
481
		if ( count( $permErrors ) ) {
482
			wfDebug( __METHOD__ . ": denied on secondary read check\n" );
483
			throw new PermissionsError( 'read', $permErrors );
484
		}
485
486
		$outputPage = $this->getContext()->getOutput();
487
		# getOldID() may as well want us to redirect somewhere else
488
		if ( $this->mRedirectUrl ) {
489
			$outputPage->redirect( $this->mRedirectUrl );
0 ignored issues
show
Bug introduced by
It seems like $this->mRedirectUrl can also be of type boolean; however, OutputPage::redirect() does only seem to accept string, 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...
490
			wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
491
492
			return;
493
		}
494
495
		# If we got diff in the query, we want to see a diff page instead of the article.
496
		if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
497
			wfDebug( __METHOD__ . ": showing diff page\n" );
498
			$this->showDiffPage();
499
500
			return;
501
		}
502
503
		# Set page title (may be overridden by DISPLAYTITLE)
504
		$outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
505
506
		$outputPage->setArticleFlag( true );
507
		# Allow frames by default
508
		$outputPage->allowClickjacking();
509
510
		$parserCache = ParserCache::singleton();
511
512
		$parserOptions = $this->getParserOptions();
513
		# Render printable version, use printable version cache
514
		if ( $outputPage->isPrintable() ) {
515
			$parserOptions->setIsPrintable( true );
516
			$parserOptions->setEditSection( false );
517
		} elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) {
518
			$parserOptions->setEditSection( false );
519
		}
520
521
		# Try client and file cache
522
		if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
523
			if ( $wgUseETag ) {
524
				$outputPage->setETag( $parserCache->getETag( $this->mPage, $parserOptions ) );
525
			}
526
527
			# Use the greatest of the page's timestamp or the timestamp of any
528
			# redirect in the chain (bug 67849)
529
			$timestamp = $this->mPage->getTouched();
530
			if ( isset( $this->mRedirectedFrom ) ) {
531
				$timestamp = max( $timestamp, $this->mRedirectedFrom->getTouched() );
532
533
				# If there can be more than one redirect in the chain, we have
534
				# to go through the whole chain too in case an intermediate
535
				# redirect was changed.
536
				if ( $wgMaxRedirects > 1 ) {
537
					$titles = Revision::newFromTitle( $this->mRedirectedFrom )
538
						->getContent( Revision::FOR_THIS_USER, $user )
539
						->getRedirectChain();
540
					$thisTitle = $this->getTitle();
541
					foreach ( $titles as $title ) {
542
						if ( Title::compare( $title, $thisTitle ) === 0 ) {
543
							break;
544
						}
545
						$timestamp = max( $timestamp, $title->getTouched() );
546
					}
547
				}
548
			}
549
550
			# Is it client cached?
551
			if ( $outputPage->checkLastModified( $timestamp ) ) {
552
				wfDebug( __METHOD__ . ": done 304\n" );
553
554
				return;
555
			# Try file cache
556
			} elseif ( $wgUseFileCache && $this->tryFileCache() ) {
557
				wfDebug( __METHOD__ . ": done file cache\n" );
558
				# tell wgOut that output is taken care of
559
				$outputPage->disable();
560
				$this->mPage->doViewUpdates( $user, $oldid );
561
562
				return;
563
			}
564
		}
565
566
		# Should the parser cache be used?
567
		$useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
568
		wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
569
		if ( $user->getStubThreshold() ) {
570
			$this->getContext()->getStats()->increment( 'pcache_miss_stub' );
571
		}
572
573
		$this->showRedirectedFromHeader();
574
		$this->showNamespaceHeader();
575
576
		# Iterate through the possible ways of constructing the output text.
577
		# Keep going until $outputDone is set, or we run out of things to do.
578
		$pass = 0;
579
		$outputDone = false;
580
		$this->mParserOutput = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type object<ParserOutput> of property $mParserOutput.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
581
582
		while ( !$outputDone && ++$pass ) {
583
			switch ( $pass ) {
584
				case 1:
585
					Hooks::run( 'ArticleViewHeader', [ &$this, &$outputDone, &$useParserCache ] );
586
					break;
587
				case 2:
588
					# Early abort if the page doesn't exist
589
					if ( !$this->mPage->exists() ) {
590
						wfDebug( __METHOD__ . ": showing missing article\n" );
591
						$this->showMissingArticle();
592
						$this->mPage->doViewUpdates( $user );
593
						return;
594
					}
595
596
					# Try the parser cache
597
					if ( $useParserCache ) {
598
						$this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
0 ignored issues
show
Documentation Bug introduced by
It seems like $parserCache->get($this->mPage, $parserOptions) can also be of type boolean. However, the property $mParserOutput is declared as type object<ParserOutput>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
599
600
						if ( $this->mParserOutput !== false ) {
601
							if ( $oldid ) {
602
								wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
603
								$this->setOldSubtitle( $oldid );
604
							} else {
605
								wfDebug( __METHOD__ . ": showing parser cache contents\n" );
606
							}
607
							$outputPage->addParserOutput( $this->mParserOutput );
0 ignored issues
show
Bug introduced by
It seems like $this->mParserOutput can also be of type boolean; however, OutputPage::addParserOutput() does only seem to accept object<ParserOutput>, 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...
608
							# Ensure that UI elements requiring revision ID have
609
							# the correct version information.
610
							$outputPage->setRevisionId( $this->mPage->getLatest() );
611
							# Preload timestamp to avoid a DB hit
612
							$cachedTimestamp = $this->mParserOutput->getTimestamp();
613
							if ( $cachedTimestamp !== null ) {
614
								$outputPage->setRevisionTimestamp( $cachedTimestamp );
615
								$this->mPage->setTimestamp( $cachedTimestamp );
616
							}
617
							$outputDone = true;
618
						}
619
					}
620
					break;
621
				case 3:
622
					# This will set $this->mRevision if needed
623
					$this->fetchContentObject();
624
625
					# Are we looking at an old revision
626
					if ( $oldid && $this->mRevision ) {
627
						$this->setOldSubtitle( $oldid );
628
629
						if ( !$this->showDeletedRevisionHeader() ) {
630
							wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
631
							return;
632
						}
633
					}
634
635
					# Ensure that UI elements requiring revision ID have
636
					# the correct version information.
637
					$outputPage->setRevisionId( $this->getRevIdFetched() );
638
					# Preload timestamp to avoid a DB hit
639
					$outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
0 ignored issues
show
Security Bug introduced by
It seems like $this->mPage->getTimestamp() targeting WikiPage::getTimestamp() can also be of type false; however, OutputPage::setRevisionTimestamp() does only seem to accept string|null, did you maybe forget to handle an error condition?
Loading history...
640
641
					# Pages containing custom CSS or JavaScript get special treatment
642
					if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
643
						wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
644
						$this->showCssOrJsPage();
645
						$outputDone = true;
646
					} elseif ( !Hooks::run( 'ArticleContentViewCustom',
647
							[ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
648
649
						# Allow extensions do their own custom view for certain pages
650
						$outputDone = true;
651
					} elseif ( !ContentHandler::runLegacyHooks( 'ArticleViewCustom',
652
							[ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
653
654
						# Allow extensions do their own custom view for certain pages
655
						$outputDone = true;
656
					}
657
					break;
658
				case 4:
659
					# Run the parse, protected by a pool counter
660
					wfDebug( __METHOD__ . ": doing uncached parse\n" );
661
662
					$content = $this->getContentObject();
663
					$poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
664
						$this->getRevIdFetched(), $useParserCache, $content );
665
666
					if ( !$poolArticleView->execute() ) {
667
						$error = $poolArticleView->getError();
668
						if ( $error ) {
669
							$outputPage->clearHTML(); // for release() errors
670
							$outputPage->enableClientCache( false );
671
							$outputPage->setRobotPolicy( 'noindex,nofollow' );
672
673
							$errortext = $error->getWikiText( false, 'view-pool-error' );
674
							$outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
675
						}
676
						# Connection or timeout error
677
						return;
678
					}
679
680
					$this->mParserOutput = $poolArticleView->getParserOutput();
681
					$outputPage->addParserOutput( $this->mParserOutput );
682
					if ( $content->getRedirectTarget() ) {
683
						$outputPage->addSubtitle( "<span id=\"redirectsub\">" .
684
							$this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
685
					}
686
687
					# Don't cache a dirty ParserOutput object
688
					if ( $poolArticleView->getIsDirty() ) {
689
						$outputPage->setCdnMaxage( 0 );
690
						$outputPage->addHTML( "<!-- parser cache is expired, " .
691
							"sending anyway due to pool overload-->\n" );
692
					}
693
694
					$outputDone = true;
695
					break;
696
				# Should be unreachable, but just in case...
697
				default:
698
					break 2;
699
			}
700
		}
701
702
		# Get the ParserOutput actually *displayed* here.
703
		# Note that $this->mParserOutput is the *current*/oldid version output.
704
		$pOutput = ( $outputDone instanceof ParserOutput )
705
			? $outputDone // object fetched by hook
706
			: $this->mParserOutput;
707
708
		# Adjust title for main page & pages with displaytitle
709
		if ( $pOutput ) {
710
			$this->adjustDisplayTitle( $pOutput );
0 ignored issues
show
Bug introduced by
It seems like $pOutput defined by $outputDone instanceof \... : $this->mParserOutput on line 704 can also be of type boolean; however, Article::adjustDisplayTitle() does only seem to accept object<ParserOutput>, 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...
711
		}
712
713
		# For the main page, overwrite the <title> element with the con-
714
		# tents of 'pagetitle-view-mainpage' instead of the default (if
715
		# that's not empty).
716
		# This message always exists because it is in the i18n files
717
		if ( $this->getTitle()->isMainPage() ) {
718
			$msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
719
			if ( !$msg->isDisabled() ) {
720
				$outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
721
			}
722
		}
723
724
		# Check for any __NOINDEX__ tags on the page using $pOutput
725
		$policy = $this->getRobotPolicy( 'view', $pOutput );
0 ignored issues
show
Bug introduced by
It seems like $pOutput defined by $outputDone instanceof \... : $this->mParserOutput on line 704 can also be of type boolean; however, Article::getRobotPolicy() does only seem to accept object<ParserOutput>|null, 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...
726
		$outputPage->setIndexPolicy( $policy['index'] );
727
		$outputPage->setFollowPolicy( $policy['follow'] );
728
729
		$this->showViewFooter();
730
		$this->mPage->doViewUpdates( $user, $oldid );
731
732
		$outputPage->addModules( 'mediawiki.action.view.postEdit' );
733
734
	}
735
736
	/**
737
	 * Adjust title for pages with displaytitle, -{T|}- or language conversion
738
	 * @param ParserOutput $pOutput
739
	 */
740
	public function adjustDisplayTitle( ParserOutput $pOutput ) {
741
		# Adjust the title if it was set by displaytitle, -{T|}- or language conversion
742
		$titleText = $pOutput->getTitleText();
743
		if ( strval( $titleText ) !== '' ) {
744
			$this->getContext()->getOutput()->setPageTitle( $titleText );
745
		}
746
	}
747
748
	/**
749
	 * Show a diff page according to current request variables. For use within
750
	 * Article::view() only, other callers should use the DifferenceEngine class.
751
	 *
752
	 */
753
	protected function showDiffPage() {
754
		$request = $this->getContext()->getRequest();
755
		$user = $this->getContext()->getUser();
756
		$diff = $request->getVal( 'diff' );
757
		$rcid = $request->getVal( 'rcid' );
758
		$diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
759
		$purge = $request->getVal( 'action' ) == 'purge';
760
		$unhide = $request->getInt( 'unhide' ) == 1;
761
		$oldid = $this->getOldID();
762
763
		$rev = $this->getRevisionFetched();
764
765
		if ( !$rev ) {
766
			$this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
767
			$msg = $this->getContext()->msg( 'difference-missing-revision' )
768
				->params( $oldid )
769
				->numParams( 1 )
770
				->parseAsBlock();
771
			$this->getContext()->getOutput()->addHTML( $msg );
772
			return;
773
		}
774
775
		$contentHandler = $rev->getContentHandler();
776
		$de = $contentHandler->createDifferenceEngine(
777
			$this->getContext(),
778
			$oldid,
779
			$diff,
780
			$rcid,
781
			$purge,
782
			$unhide
783
		);
784
785
		// DifferenceEngine directly fetched the revision:
786
		$this->mRevIdFetched = $de->mNewid;
787
		$de->showDiffPage( $diffOnly );
788
789
		// Run view updates for the newer revision being diffed (and shown
790
		// below the diff if not $diffOnly).
791
		list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
0 ignored issues
show
Unused Code introduced by
The assignment to $old is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
792
		// New can be false, convert it to 0 - this conveniently means the latest revision
793
		$this->mPage->doViewUpdates( $user, (int)$new );
794
	}
795
796
	/**
797
	 * Show a page view for a page formatted as CSS or JavaScript. To be called by
798
	 * Article::view() only.
799
	 *
800
	 * This exists mostly to serve the deprecated ShowRawCssJs hook (used to customize these views).
801
	 * It has been replaced by the ContentGetParserOutput hook, which lets you do the same but with
802
	 * more flexibility.
803
	 *
804
	 * @param bool $showCacheHint Whether to show a message telling the user
805
	 *   to clear the browser cache (default: true).
806
	 */
807
	protected function showCssOrJsPage( $showCacheHint = true ) {
808
		$outputPage = $this->getContext()->getOutput();
809
810
		if ( $showCacheHint ) {
811
			$dir = $this->getContext()->getLanguage()->getDir();
812
			$lang = $this->getContext()->getLanguage()->getHtmlCode();
813
814
			$outputPage->wrapWikiMsg(
815
				"<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
816
				'clearyourcache'
817
			);
818
		}
819
820
		$this->fetchContentObject();
821
822
		if ( $this->mContentObject ) {
823
			// Give hooks a chance to customise the output
824
			if ( ContentHandler::runLegacyHooks(
825
				'ShowRawCssJs',
826
				[ $this->mContentObject, $this->getTitle(), $outputPage ] )
827
			) {
828
				// If no legacy hooks ran, display the content of the parser output, including RL modules,
829
				// but excluding metadata like categories and language links
830
				$po = $this->mContentObject->getParserOutput( $this->getTitle() );
831
				$outputPage->addParserOutputContent( $po );
832
			}
833
		}
834
	}
835
836
	/**
837
	 * Get the robot policy to be used for the current view
838
	 * @param string $action The action= GET parameter
839
	 * @param ParserOutput|null $pOutput
840
	 * @return array The policy that should be set
841
	 * @todo actions other than 'view'
842
	 */
843
	public function getRobotPolicy( $action, $pOutput = null ) {
844
		global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy;
845
846
		$ns = $this->getTitle()->getNamespace();
847
848
		# Don't index user and user talk pages for blocked users (bug 11443)
849
		if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
850
			$specificTarget = null;
851
			$vagueTarget = null;
852
			$titleText = $this->getTitle()->getText();
853
			if ( IP::isValid( $titleText ) ) {
854
				$vagueTarget = $titleText;
855
			} else {
856
				$specificTarget = $titleText;
857
			}
858
			if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
859
				return [
860
					'index' => 'noindex',
861
					'follow' => 'nofollow'
862
				];
863
			}
864
		}
865
866
		if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
867
			# Non-articles (special pages etc), and old revisions
868
			return [
869
				'index' => 'noindex',
870
				'follow' => 'nofollow'
871
			];
872
		} elseif ( $this->getContext()->getOutput()->isPrintable() ) {
873
			# Discourage indexing of printable versions, but encourage following
874
			return [
875
				'index' => 'noindex',
876
				'follow' => 'follow'
877
			];
878
		} elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
879
			# For ?curid=x urls, disallow indexing
880
			return [
881
				'index' => 'noindex',
882
				'follow' => 'follow'
883
			];
884
		}
885
886
		# Otherwise, construct the policy based on the various config variables.
887
		$policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
888
889
		if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
890
			# Honour customised robot policies for this namespace
891
			$policy = array_merge(
892
				$policy,
893
				self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
894
			);
895
		}
896
		if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
897
			# __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
898
			# a final sanity check that we have really got the parser output.
899
			$policy = array_merge(
900
				$policy,
901
				[ 'index' => $pOutput->getIndexPolicy() ]
902
			);
903
		}
904
905
		if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
906
			# (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
907
			$policy = array_merge(
908
				$policy,
909
				self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
910
			);
911
		}
912
913
		return $policy;
914
	}
915
916
	/**
917
	 * Converts a String robot policy into an associative array, to allow
918
	 * merging of several policies using array_merge().
919
	 * @param array|string $policy Returns empty array on null/false/'', transparent
920
	 *   to already-converted arrays, converts string.
921
	 * @return array 'index' => \<indexpolicy\>, 'follow' => \<followpolicy\>
922
	 */
923
	public static function formatRobotPolicy( $policy ) {
924
		if ( is_array( $policy ) ) {
925
			return $policy;
926
		} elseif ( !$policy ) {
927
			return [];
928
		}
929
930
		$policy = explode( ',', $policy );
931
		$policy = array_map( 'trim', $policy );
932
933
		$arr = [];
934
		foreach ( $policy as $var ) {
935
			if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
936
				$arr['index'] = $var;
937
			} elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
938
				$arr['follow'] = $var;
939
			}
940
		}
941
942
		return $arr;
943
	}
944
945
	/**
946
	 * If this request is a redirect view, send "redirected from" subtitle to
947
	 * the output. Returns true if the header was needed, false if this is not
948
	 * a redirect view. Handles both local and remote redirects.
949
	 *
950
	 * @return bool
951
	 */
952
	public function showRedirectedFromHeader() {
953
		global $wgRedirectSources;
954
955
		$context = $this->getContext();
956
		$outputPage = $context->getOutput();
957
		$request = $context->getRequest();
958
		$rdfrom = $request->getVal( 'rdfrom' );
959
960
		// Construct a URL for the current page view, but with the target title
961
		$query = $request->getValues();
962
		unset( $query['rdfrom'] );
963
		unset( $query['title'] );
964
		if ( $this->getTitle()->isRedirect() ) {
965
			// Prevent double redirects
966
			$query['redirect'] = 'no';
967
		}
968
		$redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
969
970
		if ( isset( $this->mRedirectedFrom ) ) {
971
			// This is an internally redirected page view.
972
			// We'll need a backlink to the source page for navigation.
973
			if ( Hooks::run( 'ArticleViewRedirect', [ &$this ] ) ) {
974
				$redir = Linker::linkKnown(
975
					$this->mRedirectedFrom,
976
					null,
977
					[],
978
					[ 'redirect' => 'no' ]
979
				);
980
981
				$outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
982
					$context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
983
				. "</span>" );
984
985
				// Add the script to update the displayed URL and
986
				// set the fragment if one was specified in the redirect
987
				$outputPage->addJsConfigVars( [
988
					'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
989
				] );
990
				$outputPage->addModules( 'mediawiki.action.view.redirect' );
991
992
				// Add a <link rel="canonical"> tag
993
				$outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
0 ignored issues
show
Security Bug introduced by
It seems like $this->getTitle()->getCanonicalURL() targeting Title::getCanonicalURL() can also be of type false; however, OutputPage::setCanonicalUrl() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
994
995
				// Tell the output object that the user arrived at this article through a redirect
996
				$outputPage->setRedirectedFrom( $this->mRedirectedFrom );
997
998
				return true;
999
			}
1000
		} elseif ( $rdfrom ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rdfrom of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1001
			// This is an externally redirected view, from some other wiki.
1002
			// If it was reported from a trusted site, supply a backlink.
1003
			if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
1004
				$redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
1005
				$outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1006
					$context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1007
				. "</span>" );
1008
1009
				// Add the script to update the displayed URL
1010
				$outputPage->addJsConfigVars( [
1011
					'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1012
				] );
1013
				$outputPage->addModules( 'mediawiki.action.view.redirect' );
1014
1015
				return true;
1016
			}
1017
		}
1018
1019
		return false;
1020
	}
1021
1022
	/**
1023
	 * Show a header specific to the namespace currently being viewed, like
1024
	 * [[MediaWiki:Talkpagetext]]. For Article::view().
1025
	 */
1026
	public function showNamespaceHeader() {
1027
		if ( $this->getTitle()->isTalkPage() ) {
1028
			if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
1029
				$this->getContext()->getOutput()->wrapWikiMsg(
1030
					"<div class=\"mw-talkpageheader\">\n$1\n</div>",
1031
					[ 'talkpageheader' ]
1032
				);
1033
			}
1034
		}
1035
	}
1036
1037
	/**
1038
	 * Show the footer section of an ordinary page view
1039
	 */
1040
	public function showViewFooter() {
1041
		# check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1042
		if ( $this->getTitle()->getNamespace() == NS_USER_TALK
1043
			&& IP::isValid( $this->getTitle()->getText() )
1044
		) {
1045
			$this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1046
		}
1047
1048
		// Show a footer allowing the user to patrol the shown revision or page if possible
1049
		$patrolFooterShown = $this->showPatrolFooter();
1050
1051
		Hooks::run( 'ArticleViewFooter', [ $this, $patrolFooterShown ] );
1052
	}
1053
1054
	/**
1055
	 * If patrol is possible, output a patrol UI box. This is called from the
1056
	 * footer section of ordinary page views. If patrol is not possible or not
1057
	 * desired, does nothing.
1058
	 * Side effect: When the patrol link is build, this method will call
1059
	 * OutputPage::preventClickjacking() and load mediawiki.page.patrol.ajax.
1060
	 *
1061
	 * @return bool
1062
	 */
1063
	public function showPatrolFooter() {
1064
		global $wgUseNPPatrol, $wgUseRCPatrol, $wgUseFilePatrol, $wgEnableAPI, $wgEnableWriteAPI;
1065
1066
		$outputPage = $this->getContext()->getOutput();
1067
		$user = $this->getContext()->getUser();
1068
		$title = $this->getTitle();
1069
		$rc = false;
1070
1071
		if ( !$title->quickUserCan( 'patrol', $user )
1072
			|| !( $wgUseRCPatrol || $wgUseNPPatrol
1073
				|| ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1074
		) {
1075
			// Patrolling is disabled or the user isn't allowed to
1076
			return false;
1077
		}
1078
1079
		if ( $this->mRevision
1080
			&& !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
1081
		) {
1082
			// The current revision is already older than what could be in the RC table
1083
			// 6h tolerance because the RC might not be cleaned out regularly
1084
			return false;
1085
		}
1086
1087
		// Check for cached results
1088
		$key = wfMemcKey( 'unpatrollable-page', $title->getArticleID() );
1089
		$cache = ObjectCache::getMainWANInstance();
1090
		if ( $cache->get( $key ) ) {
1091
			return false;
1092
		}
1093
1094
		$dbr = wfGetDB( DB_SLAVE );
1095
		$oldestRevisionTimestamp = $dbr->selectField(
1096
			'revision',
1097
			'MIN( rev_timestamp )',
1098
			[ 'rev_page' => $title->getArticleID() ],
1099
			__METHOD__
1100
		);
1101
1102
		// New page patrol: Get the timestamp of the oldest revison which
1103
		// the revision table holds for the given page. Then we look
1104
		// whether it's within the RC lifespan and if it is, we try
1105
		// to get the recentchanges row belonging to that entry
1106
		// (with rc_new = 1).
1107
		$recentPageCreation = false;
1108
		if ( $oldestRevisionTimestamp
1109
			&& RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1110
		) {
1111
			// 6h tolerance because the RC might not be cleaned out regularly
1112
			$recentPageCreation = true;
1113
			$rc = RecentChange::newFromConds(
1114
				[
1115
					'rc_new' => 1,
1116
					'rc_timestamp' => $oldestRevisionTimestamp,
1117
					'rc_namespace' => $title->getNamespace(),
1118
					'rc_cur_id' => $title->getArticleID()
1119
				],
1120
				__METHOD__
1121
			);
1122
			if ( $rc ) {
1123
				// Use generic patrol message for new pages
1124
				$markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1125
			}
1126
		}
1127
1128
		// File patrol: Get the timestamp of the latest upload for this page,
1129
		// check whether it is within the RC lifespan and if it is, we try
1130
		// to get the recentchanges row belonging to that entry
1131
		// (with rc_type = RC_LOG, rc_log_type = upload).
1132
		$recentFileUpload = false;
1133
		if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1134
			&& $title->getNamespace() === NS_FILE ) {
1135
			// Retrieve timestamp of most recent upload
1136
			$newestUploadTimestamp = $dbr->selectField(
1137
				'image',
1138
				'MAX( img_timestamp )',
1139
				[ 'img_name' => $title->getDBkey() ],
1140
				__METHOD__
1141
			);
1142
			if ( $newestUploadTimestamp
1143
				&& RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1144
			) {
1145
				// 6h tolerance because the RC might not be cleaned out regularly
1146
				$recentFileUpload = true;
1147
				$rc = RecentChange::newFromConds(
1148
					[
1149
						'rc_type' => RC_LOG,
1150
						'rc_log_type' => 'upload',
1151
						'rc_timestamp' => $newestUploadTimestamp,
1152
						'rc_namespace' => NS_FILE,
1153
						'rc_cur_id' => $title->getArticleID()
1154
					],
1155
					__METHOD__,
1156
					[ 'USE INDEX' => 'rc_timestamp' ]
1157
				);
1158
				if ( $rc ) {
1159
					// Use patrol message specific to files
1160
					$markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1161
				}
1162
			}
1163
		}
1164
1165
		if ( !$recentPageCreation && !$recentFileUpload ) {
1166
			// Page creation and latest upload (for files) is too old to be in RC
1167
1168
			// We definitely can't patrol so cache the information
1169
			// When a new file version is uploaded, the cache is cleared
1170
			$cache->set( $key, '1' );
1171
1172
			return false;
1173
		}
1174
1175
		if ( !$rc ) {
1176
			// Don't cache: This can be hit if the page gets accessed very fast after
1177
			// its creation / latest upload or in case we have high slave lag. In case
1178
			// the revision is too old, we will already return above.
1179
			return false;
1180
		}
1181
1182
		if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1183
			// Patrolled RC entry around
1184
1185
			// Cache the information we gathered above in case we can't patrol
1186
			// Don't cache in case we can patrol as this could change
1187
			$cache->set( $key, '1' );
1188
1189
			return false;
1190
		}
1191
1192
		if ( $rc->getPerformer()->equals( $user ) ) {
1193
			// Don't show a patrol link for own creations/uploads. If the user could
1194
			// patrol them, they already would be patrolled
1195
			return false;
1196
		}
1197
1198
		$rcid = $rc->getAttribute( 'rc_id' );
1199
1200
		$token = $user->getEditToken( $rcid );
1201
1202
		$outputPage->preventClickjacking();
1203
		if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
1204
			$outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1205
		}
1206
1207
		$link = Linker::linkKnown(
1208
			$title,
1209
			$markPatrolledMsg->escaped(),
0 ignored issues
show
Bug introduced by
The variable $markPatrolledMsg does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1210
			[],
1211
			[
1212
				'action' => 'markpatrolled',
1213
				'rcid' => $rcid,
1214
				'token' => $token,
1215
			]
1216
		);
1217
1218
		$outputPage->addHTML(
1219
			"<div class='patrollink'>" .
1220
				wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1221
			'</div>'
1222
		);
1223
1224
		return true;
1225
	}
1226
1227
	/**
1228
	 * Purge the cache used to check if it is worth showing the patrol footer
1229
	 * For example, it is done during re-uploads when file patrol is used.
1230
	 * @param int $articleID ID of the article to purge
1231
	 * @since 1.27
1232
	 */
1233
	public static function purgePatrolFooterCache( $articleID ) {
1234
		$cache = ObjectCache::getMainWANInstance();
1235
		$cache->delete( wfMemcKey( 'unpatrollable-page', $articleID ) );
1236
	}
1237
1238
	/**
1239
	 * Show the error text for a missing article. For articles in the MediaWiki
1240
	 * namespace, show the default message text. To be called from Article::view().
1241
	 */
1242
	public function showMissingArticle() {
1243
		global $wgSend404Code;
1244
1245
		$outputPage = $this->getContext()->getOutput();
1246
		// Whether the page is a root user page of an existing user (but not a subpage)
1247
		$validUserPage = false;
1248
1249
		$title = $this->getTitle();
1250
1251
		# Show info in user (talk) namespace. Does the user exist? Is he blocked?
1252
		if ( $title->getNamespace() == NS_USER
1253
			|| $title->getNamespace() == NS_USER_TALK
1254
		) {
1255
			$rootPart = explode( '/', $title->getText() )[0];
1256
			$user = User::newFromName( $rootPart, false /* allow IP users*/ );
1257
			$ip = User::isIP( $rootPart );
1258
			$block = Block::newFromTarget( $user, $user );
0 ignored issues
show
Security Bug introduced by
It seems like $user defined by \User::newFromName($rootPart, false) on line 1256 can also be of type false; however, Block::newFromTarget() does only seem to accept string|object<User>|integer, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
Security Bug introduced by
It seems like $user defined by \User::newFromName($rootPart, false) on line 1256 can also be of type false; however, Block::newFromTarget() does only seem to accept string|object<User>|integer|null, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1259
1260
			if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1261
				$outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1262
					[ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1263 View Code Duplication
			} elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
1264
				# Show log extract if the user is currently blocked
1265
				LogEventsList::showLogExtract(
1266
					$outputPage,
1267
					'block',
1268
					MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
1269
					'',
1270
					[
1271
						'lim' => 1,
1272
						'showIfEmpty' => false,
1273
						'msgKey' => [
1274
							'blocked-notice-logextract',
1275
							$user->getName() # Support GENDER in notice
1276
						]
1277
					]
1278
				);
1279
				$validUserPage = !$title->isSubpage();
1280
			} else {
1281
				$validUserPage = !$title->isSubpage();
1282
			}
1283
		}
1284
1285
		Hooks::run( 'ShowMissingArticle', [ $this ] );
1286
1287
		# Show delete and move logs if there were any such events.
1288
		# The logging query can DOS the site when bots/crawlers cause 404 floods,
1289
		# so be careful showing this. 404 pages must be cheap as they are hard to cache.
1290
		$cache = ObjectCache::getMainStashInstance();
1291
		$key = wfMemcKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1292
		$loggedIn = $this->getContext()->getUser()->isLoggedIn();
1293
		if ( $loggedIn || $cache->get( $key ) ) {
1294
			$logTypes = [ 'delete', 'move' ];
1295
			$conds = [ "log_action != 'revision'" ];
1296
			// Give extensions a chance to hide their (unrelated) log entries
1297
			Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
1298
			LogEventsList::showLogExtract(
1299
				$outputPage,
1300
				$logTypes,
1301
				$title,
1302
				'',
1303
				[
1304
					'lim' => 10,
1305
					'conds' => $conds,
1306
					'showIfEmpty' => false,
1307
					'msgKey' => [ $loggedIn
1308
						? 'moveddeleted-notice'
1309
						: 'moveddeleted-notice-recent'
1310
					]
1311
				]
1312
			);
1313
		}
1314
1315
		if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1316
			// If there's no backing content, send a 404 Not Found
1317
			// for better machine handling of broken links.
1318
			$this->getContext()->getRequest()->response()->statusHeader( 404 );
1319
		}
1320
1321
		// Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1322
		$policy = $this->getRobotPolicy( 'view' );
1323
		$outputPage->setIndexPolicy( $policy['index'] );
1324
		$outputPage->setFollowPolicy( $policy['follow'] );
1325
1326
		$hookResult = Hooks::run( 'BeforeDisplayNoArticleText', [ $this ] );
1327
1328
		if ( !$hookResult ) {
1329
			return;
1330
		}
1331
1332
		# Show error message
1333
		$oldid = $this->getOldID();
1334
		if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1335
			$outputPage->addParserOutput( $this->getContentObject()->getParserOutput( $title ) );
1336
		} else {
1337
			if ( $oldid ) {
1338
				$text = wfMessage( 'missing-revision', $oldid )->plain();
1339
			} elseif ( $title->quickUserCan( 'create', $this->getContext()->getUser() )
1340
				&& $title->quickUserCan( 'edit', $this->getContext()->getUser() )
1341
			) {
1342
				$message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1343
				$text = wfMessage( $message )->plain();
1344
			} else {
1345
				$text = wfMessage( 'noarticletext-nopermission' )->plain();
1346
			}
1347
1348
			$dir = $this->getContext()->getLanguage()->getDir();
1349
			$lang = $this->getContext()->getLanguage()->getCode();
1350
			$outputPage->addWikiText( Xml::openElement( 'div', [
1351
				'class' => "noarticletext mw-content-$dir",
1352
				'dir' => $dir,
1353
				'lang' => $lang,
1354
			] ) . "\n$text\n</div>" );
1355
		}
1356
	}
1357
1358
	/**
1359
	 * If the revision requested for view is deleted, check permissions.
1360
	 * Send either an error message or a warning header to the output.
1361
	 *
1362
	 * @return bool True if the view is allowed, false if not.
1363
	 */
1364
	public function showDeletedRevisionHeader() {
1365
		if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
1366
			// Not deleted
1367
			return true;
1368
		}
1369
1370
		$outputPage = $this->getContext()->getOutput();
1371
		$user = $this->getContext()->getUser();
1372
		// If the user is not allowed to see it...
1373
		if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
1374
			$outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1375
				'rev-deleted-text-permission' );
1376
1377
			return false;
1378
		// If the user needs to confirm that they want to see it...
1379
		} elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1380
			# Give explanation and add a link to view the revision...
1381
			$oldid = intval( $this->getOldID() );
1382
			$link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1383
			$msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1384
				'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1385
			$outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1386
				[ $msg, $link ] );
1387
1388
			return false;
1389
		// We are allowed to see...
1390
		} else {
1391
			$msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1392
				'rev-suppressed-text-view' : 'rev-deleted-text-view';
1393
			$outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1394
1395
			return true;
1396
		}
1397
	}
1398
1399
	/**
1400
	 * Generate the navigation links when browsing through an article revisions
1401
	 * It shows the information as:
1402
	 *   Revision as of \<date\>; view current revision
1403
	 *   \<- Previous version | Next Version -\>
1404
	 *
1405
	 * @param int $oldid Revision ID of this article revision
1406
	 */
1407
	public function setOldSubtitle( $oldid = 0 ) {
1408
		if ( !Hooks::run( 'DisplayOldSubtitle', [ &$this, &$oldid ] ) ) {
1409
			return;
1410
		}
1411
1412
		$context = $this->getContext();
1413
		$unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1414
1415
		# Cascade unhide param in links for easy deletion browsing
1416
		$extraParams = [];
1417
		if ( $unhide ) {
1418
			$extraParams['unhide'] = 1;
1419
		}
1420
1421
		if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1422
			$revision = $this->mRevision;
1423
		} else {
1424
			$revision = Revision::newFromId( $oldid );
1425
		}
1426
1427
		$timestamp = $revision->getTimestamp();
1428
1429
		$current = ( $oldid == $this->mPage->getLatest() );
1430
		$language = $context->getLanguage();
1431
		$user = $context->getUser();
1432
1433
		$td = $language->userTimeAndDate( $timestamp, $user );
1434
		$tddate = $language->userDate( $timestamp, $user );
1435
		$tdtime = $language->userTime( $timestamp, $user );
1436
1437
		# Show user links if allowed to see them. If hidden, then show them only if requested...
1438
		$userlinks = Linker::revUserTools( $revision, !$unhide );
0 ignored issues
show
Bug introduced by
It seems like $revision defined by \Revision::newFromId($oldid) on line 1424 can be null; however, Linker::revUserTools() 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...
1439
1440
		$infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1441
			? 'revision-info-current'
1442
			: 'revision-info';
1443
1444
		$outputPage = $context->getOutput();
1445
		$outputPage->addSubtitle( "<div id=\"mw-{$infomsg}\">" .
1446
			$context->msg( $infomsg, $td )
1447
				->rawParams( $userlinks )
1448
				->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
1449
				->rawParams( Linker::revComment( $revision, true, true ) )
0 ignored issues
show
Bug introduced by
It seems like $revision defined by \Revision::newFromId($oldid) on line 1424 can be null; however, Linker::revComment() 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...
1450
				->parse() .
1451
			"</div>"
1452
		);
1453
1454
		$lnk = $current
1455
			? $context->msg( 'currentrevisionlink' )->escaped()
1456
			: Linker::linkKnown(
1457
				$this->getTitle(),
1458
				$context->msg( 'currentrevisionlink' )->escaped(),
1459
				[],
1460
				$extraParams
1461
			);
1462
		$curdiff = $current
1463
			? $context->msg( 'diff' )->escaped()
1464
			: Linker::linkKnown(
1465
				$this->getTitle(),
1466
				$context->msg( 'diff' )->escaped(),
1467
				[],
1468
				[
1469
					'diff' => 'cur',
1470
					'oldid' => $oldid
1471
				] + $extraParams
1472
			);
1473
		$prev = $this->getTitle()->getPreviousRevisionID( $oldid );
1474
		$prevlink = $prev
1475
			? Linker::linkKnown(
1476
				$this->getTitle(),
1477
				$context->msg( 'previousrevision' )->escaped(),
1478
				[],
1479
				[
1480
					'direction' => 'prev',
1481
					'oldid' => $oldid
1482
				] + $extraParams
1483
			)
1484
			: $context->msg( 'previousrevision' )->escaped();
1485
		$prevdiff = $prev
1486
			? Linker::linkKnown(
1487
				$this->getTitle(),
1488
				$context->msg( 'diff' )->escaped(),
1489
				[],
1490
				[
1491
					'diff' => 'prev',
1492
					'oldid' => $oldid
1493
				] + $extraParams
1494
			)
1495
			: $context->msg( 'diff' )->escaped();
1496
		$nextlink = $current
1497
			? $context->msg( 'nextrevision' )->escaped()
1498
			: Linker::linkKnown(
1499
				$this->getTitle(),
1500
				$context->msg( 'nextrevision' )->escaped(),
1501
				[],
1502
				[
1503
					'direction' => 'next',
1504
					'oldid' => $oldid
1505
				] + $extraParams
1506
			);
1507
		$nextdiff = $current
1508
			? $context->msg( 'diff' )->escaped()
1509
			: Linker::linkKnown(
1510
				$this->getTitle(),
1511
				$context->msg( 'diff' )->escaped(),
1512
				[],
1513
				[
1514
					'diff' => 'next',
1515
					'oldid' => $oldid
1516
				] + $extraParams
1517
			);
1518
1519
		$cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
0 ignored issues
show
Bug introduced by
It seems like $revision defined by \Revision::newFromId($oldid) on line 1424 can be null; however, Linker::getRevDeleteLink() 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...
1520
		if ( $cdel !== '' ) {
1521
			$cdel .= ' ';
1522
		}
1523
1524
		$outputPage->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel .
1525
			$context->msg( 'revision-nav' )->rawParams(
1526
				$prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1527
			)->escaped() . "</div>" );
1528
	}
1529
1530
	/**
1531
	 * Return the HTML for the top of a redirect page
1532
	 *
1533
	 * Chances are you should just be using the ParserOutput from
1534
	 * WikitextContent::getParserOutput instead of calling this for redirects.
1535
	 *
1536
	 * @param Title|array $target Destination(s) to redirect
1537
	 * @param bool $appendSubtitle [optional]
1538
	 * @param bool $forceKnown Should the image be shown as a bluelink regardless of existence?
1539
	 * @return string Containing HTML with redirect link
1540
	 */
1541
	public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1542
		$lang = $this->getTitle()->getPageLanguage();
1543
		$out = $this->getContext()->getOutput();
1544
		if ( $appendSubtitle ) {
1545
			$out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1546
		}
1547
		$out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1548
		return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1549
	}
1550
1551
	/**
1552
	 * Return the HTML for the top of a redirect page
1553
	 *
1554
	 * Chances are you should just be using the ParserOutput from
1555
	 * WikitextContent::getParserOutput instead of calling this for redirects.
1556
	 *
1557
	 * @since 1.23
1558
	 * @param Language $lang
1559
	 * @param Title|array $target Destination(s) to redirect
1560
	 * @param bool $forceKnown Should the image be shown as a bluelink regardless of existence?
1561
	 * @return string Containing HTML with redirect link
1562
	 */
1563
	public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1564
		if ( !is_array( $target ) ) {
1565
			$target = [ $target ];
1566
		}
1567
1568
		$html = '<ul class="redirectText">';
1569
		/** @var Title $title */
1570
		foreach ( $target as $title ) {
1571
			$html .= '<li>' . Linker::link(
1572
				$title,
1573
				htmlspecialchars( $title->getFullText() ),
1574
				[],
1575
				// Make sure wiki page redirects are not followed
1576
				$title->isRedirect() ? [ 'redirect' => 'no' ] : [],
1577
				( $forceKnown ? [ 'known', 'noclasses' ] : [] )
1578
			) . '</li>';
1579
		}
1580
		$html .= '</ul>';
1581
1582
		$redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1583
1584
		return '<div class="redirectMsg">' .
1585
			'<p>' . $redirectToText . '</p>' .
1586
			$html .
1587
			'</div>';
1588
	}
1589
1590
	/**
1591
	 * Adds help link with an icon via page indicators.
1592
	 * Link target can be overridden by a local message containing a wikilink:
1593
	 * the message key is: 'namespace-' + namespace number + '-helppage'.
1594
	 * @param string $to Target MediaWiki.org page title or encoded URL.
1595
	 * @param bool $overrideBaseUrl Whether $url is a full URL, to avoid MW.o.
1596
	 * @since 1.25
1597
	 */
1598 View Code Duplication
	public function addHelpLink( $to, $overrideBaseUrl = false ) {
1599
		$msg = wfMessage(
1600
			'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1601
		);
1602
1603
		$out = $this->getContext()->getOutput();
1604
		if ( !$msg->isDisabled() ) {
1605
			$helpUrl = Skin::makeUrl( $msg->plain() );
1606
			$out->addHelpLink( $helpUrl, true );
1607
		} else {
1608
			$out->addHelpLink( $to, $overrideBaseUrl );
1609
		}
1610
	}
1611
1612
	/**
1613
	 * Handle action=render
1614
	 */
1615
	public function render() {
1616
		$this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1617
		$this->getContext()->getOutput()->setArticleBodyOnly( true );
1618
		$this->getContext()->getOutput()->enableSectionEditLinks( false );
1619
		$this->view();
1620
	}
1621
1622
	/**
1623
	 * action=protect handler
1624
	 */
1625
	public function protect() {
1626
		$form = new ProtectionForm( $this );
1627
		$form->execute();
1628
	}
1629
1630
	/**
1631
	 * action=unprotect handler (alias)
1632
	 */
1633
	public function unprotect() {
1634
		$this->protect();
1635
	}
1636
1637
	/**
1638
	 * UI entry point for page deletion
1639
	 */
1640
	public function delete() {
1641
		# This code desperately needs to be totally rewritten
1642
1643
		$title = $this->getTitle();
1644
		$context = $this->getContext();
1645
		$user = $context->getUser();
1646
		$request = $context->getRequest();
1647
1648
		# Check permissions
1649
		$permissionErrors = $title->getUserPermissionsErrors( 'delete', $user );
1650
		if ( count( $permissionErrors ) ) {
1651
			throw new PermissionsError( 'delete', $permissionErrors );
1652
		}
1653
1654
		# Read-only check...
1655
		if ( wfReadOnly() ) {
1656
			throw new ReadOnlyError;
1657
		}
1658
1659
		# Better double-check that it hasn't been deleted yet!
1660
		$this->mPage->loadPageData(
1661
			$request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1662
		);
1663
		if ( !$this->mPage->exists() ) {
1664
			$deleteLogPage = new LogPage( 'delete' );
1665
			$outputPage = $context->getOutput();
1666
			$outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1667
			$outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1668
					[ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1669
				);
1670
			$outputPage->addHTML(
1671
				Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1672
			);
1673
			LogEventsList::showLogExtract(
1674
				$outputPage,
1675
				'delete',
1676
				$title
1677
			);
1678
1679
			return;
1680
		}
1681
1682
		$deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1683
		$deleteReason = $request->getText( 'wpReason' );
1684
1685 View Code Duplication
		if ( $deleteReasonList == 'other' ) {
1686
			$reason = $deleteReason;
1687
		} elseif ( $deleteReason != '' ) {
1688
			// Entry from drop down menu + additional comment
1689
			$colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1690
			$reason = $deleteReasonList . $colonseparator . $deleteReason;
1691
		} else {
1692
			$reason = $deleteReasonList;
1693
		}
1694
1695
		if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1696
			[ 'delete', $this->getTitle()->getPrefixedText() ] )
1697
		) {
1698
			# Flag to hide all contents of the archived revisions
1699
			$suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
0 ignored issues
show
Bug Best Practice introduced by
The expression $request->getVal('wpSuppress') of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1700
1701
			$this->doDelete( $reason, $suppress );
1702
1703
			WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1704
1705
			return;
1706
		}
1707
1708
		// Generate deletion reason
1709
		$hasHistory = false;
1710
		if ( !$reason ) {
1711
			try {
1712
				$reason = $this->generateReason( $hasHistory );
1713
			} catch ( Exception $e ) {
1714
				# if a page is horribly broken, we still want to be able to
1715
				# delete it. So be lenient about errors here.
1716
				wfDebug( "Error while building auto delete summary: $e" );
1717
				$reason = '';
1718
			}
1719
		}
1720
1721
		// If the page has a history, insert a warning
1722
		if ( $hasHistory ) {
1723
			$title = $this->getTitle();
1724
1725
			// The following can use the real revision count as this is only being shown for users
1726
			// that can delete this page.
1727
			// This, as a side-effect, also makes sure that the following query isn't being run for
1728
			// pages with a larger history, unless the user has the 'bigdelete' right
1729
			// (and is about to delete this page).
1730
			$dbr = wfGetDB( DB_SLAVE );
1731
			$revisions = $edits = (int)$dbr->selectField(
0 ignored issues
show
Unused Code introduced by
$edits is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1732
				'revision',
1733
				'COUNT(rev_page)',
1734
				[ 'rev_page' => $title->getArticleID() ],
1735
				__METHOD__
1736
			);
1737
1738
			// @todo FIXME: i18n issue/patchwork message
1739
			$context->getOutput()->addHTML(
1740
				'<strong class="mw-delete-warning-revisions">' .
1741
				$context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1742
				$context->msg( 'word-separator' )->escaped() . Linker::linkKnown( $title,
1743
					$context->msg( 'history' )->escaped(),
1744
					[],
1745
					[ 'action' => 'history' ] ) .
1746
				'</strong>'
1747
			);
1748
1749
			if ( $title->isBigDeletion() ) {
1750
				global $wgDeleteRevisionsLimit;
1751
				$context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1752
					[
1753
						'delete-warning-toobig',
1754
						$context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1755
					]
1756
				);
1757
			}
1758
		}
1759
1760
		$this->confirmDelete( $reason );
0 ignored issues
show
Security Bug introduced by
It seems like $reason defined by $this->generateReason($hasHistory) on line 1712 can also be of type false; however, Article::confirmDelete() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1761
	}
1762
1763
	/**
1764
	 * Output deletion confirmation dialog
1765
	 * @todo FIXME: Move to another file?
1766
	 * @param string $reason Prefilled reason
1767
	 */
1768
	public function confirmDelete( $reason ) {
1769
		wfDebug( "Article::confirmDelete\n" );
1770
1771
		$title = $this->getTitle();
1772
		$ctx = $this->getContext();
1773
		$outputPage = $ctx->getOutput();
1774
		$useMediaWikiUIEverywhere = $ctx->getConfig()->get( 'UseMediaWikiUIEverywhere' );
1775
		$outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1776
		$outputPage->addBacklinkSubtitle( $title );
1777
		$outputPage->setRobotPolicy( 'noindex,nofollow' );
1778
		$backlinkCache = $title->getBacklinkCache();
1779
		if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1780
			$outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1781
				'deleting-backlinks-warning' );
1782
		}
1783
		$outputPage->addWikiMsg( 'confirmdeletetext' );
1784
1785
		Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
1786
1787
		$user = $this->getContext()->getUser();
1788
1789
		if ( $user->isAllowed( 'suppressrevision' ) ) {
1790
			$suppress = Html::openElement( 'div', [ 'id' => 'wpDeleteSuppressRow' ] ) .
1791
				Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
1792
					'wpSuppress', 'wpSuppress', false, [ 'tabindex' => '4' ] ) .
1793
				Html::closeElement( 'div' );
1794
		} else {
1795
			$suppress = '';
1796
		}
1797
		$checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1798
1799
		$form = Html::openElement( 'form', [ 'method' => 'post',
1800
			'action' => $title->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ] ) .
1801
			Html::openElement( 'fieldset', [ 'id' => 'mw-delete-table' ] ) .
1802
			Html::element( 'legend', null, wfMessage( 'delete-legend' )->text() ) .
1803
			Html::openElement( 'div', [ 'id' => 'mw-deleteconfirm-table' ] ) .
1804
			Html::openElement( 'div', [ 'id' => 'wpDeleteReasonListRow' ] ) .
1805
			Html::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
1806
			'&nbsp;' .
1807
			Xml::listDropDown(
1808
				'wpDeleteReasonList',
1809
				wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
1810
				wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(),
1811
				'',
1812
				'wpReasonDropDown',
1813
				1
1814
			) .
1815
			Html::closeElement( 'div' ) .
1816
			Html::openElement( 'div', [ 'id' => 'wpDeleteReasonRow' ] ) .
1817
			Html::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) .
1818
			'&nbsp;' .
1819
			Html::input( 'wpReason', $reason, 'text', [
1820
				'size' => '60',
1821
				'maxlength' => '255',
1822
				'tabindex' => '2',
1823
				'id' => 'wpReason',
1824
				'class' => 'mw-ui-input-inline',
1825
				'autofocus'
1826
			] ) .
1827
			Html::closeElement( 'div' );
1828
1829
		# Disallow watching if user is not logged in
1830 View Code Duplication
		if ( $user->isLoggedIn() ) {
1831
			$form .=
1832
					Xml::checkLabel( wfMessage( 'watchthis' )->text(),
1833
						'wpWatch', 'wpWatch', $checkWatch, [ 'tabindex' => '3' ] );
1834
		}
1835
1836
		$form .=
1837
				Html::openElement( 'div' ) .
1838
				$suppress .
1839
					Xml::submitButton( wfMessage( 'deletepage' )->text(),
1840
						[
1841
							'name' => 'wpConfirmB',
1842
							'id' => 'wpConfirmB',
1843
							'tabindex' => '5',
1844
							'class' => $useMediaWikiUIEverywhere ? 'mw-ui-button mw-ui-destructive' : '',
1845
						]
1846
					) .
1847
				Html::closeElement( 'div' ) .
1848
			Html::closeElement( 'div' ) .
1849
			Xml::closeElement( 'fieldset' ) .
1850
			Html::hidden(
1851
				'wpEditToken',
1852
				$user->getEditToken( [ 'delete', $title->getPrefixedText() ] )
1853
			) .
1854
			Xml::closeElement( 'form' );
1855
1856 View Code Duplication
			if ( $user->isAllowed( 'editinterface' ) ) {
1857
				$link = Linker::linkKnown(
1858
					$ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
1859
					wfMessage( 'delete-edit-reasonlist' )->escaped(),
1860
					[],
1861
					[ 'action' => 'edit' ]
1862
				);
1863
				$form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
1864
			}
1865
1866
		$outputPage->addHTML( $form );
1867
1868
		$deleteLogPage = new LogPage( 'delete' );
1869
		$outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1870
		LogEventsList::showLogExtract( $outputPage, 'delete', $title );
1871
	}
1872
1873
	/**
1874
	 * Perform a deletion and output success or failure messages
1875
	 * @param string $reason
1876
	 * @param bool $suppress
1877
	 */
1878
	public function doDelete( $reason, $suppress = false ) {
1879
		$error = '';
1880
		$context = $this->getContext();
1881
		$outputPage = $context->getOutput();
1882
		$user = $context->getUser();
1883
		$status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error, $user );
1884
1885
		if ( $status->isGood() ) {
1886
			$deleted = $this->getTitle()->getPrefixedText();
1887
1888
			$outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
1889
			$outputPage->setRobotPolicy( 'noindex,nofollow' );
1890
1891
			$loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
1892
1893
			$outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
1894
1895
			Hooks::run( 'ArticleDeleteAfterSuccess', [ $this->getTitle(), $outputPage ] );
1896
1897
			$outputPage->returnToMain( false );
1898
		} else {
1899
			$outputPage->setPageTitle(
1900
				wfMessage( 'cannotdelete-title',
1901
					$this->getTitle()->getPrefixedText() )
1902
			);
1903
1904
			if ( $error == '' ) {
1905
				$outputPage->addWikiText(
1906
					"<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
1907
				);
1908
				$deleteLogPage = new LogPage( 'delete' );
1909
				$outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1910
1911
				LogEventsList::showLogExtract(
1912
					$outputPage,
1913
					'delete',
1914
					$this->getTitle()
1915
				);
1916
			} else {
1917
				$outputPage->addHTML( $error );
1918
			}
1919
		}
1920
	}
1921
1922
	/* Caching functions */
1923
1924
	/**
1925
	 * checkLastModified returns true if it has taken care of all
1926
	 * output to the client that is necessary for this request.
1927
	 * (that is, it has sent a cached version of the page)
1928
	 *
1929
	 * @return bool True if cached version send, false otherwise
1930
	 */
1931
	protected function tryFileCache() {
1932
		static $called = false;
1933
1934
		if ( $called ) {
1935
			wfDebug( "Article::tryFileCache(): called twice!?\n" );
1936
			return false;
1937
		}
1938
1939
		$called = true;
1940
		if ( $this->isFileCacheable() ) {
1941
			$cache = new HTMLFileCache( $this->getTitle(), 'view' );
1942
			if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
1943
				wfDebug( "Article::tryFileCache(): about to load file\n" );
1944
				$cache->loadFromFileCache( $this->getContext() );
1945
				return true;
1946
			} else {
1947
				wfDebug( "Article::tryFileCache(): starting buffer\n" );
1948
				ob_start( [ &$cache, 'saveToFileCache' ] );
1949
			}
1950
		} else {
1951
			wfDebug( "Article::tryFileCache(): not cacheable\n" );
1952
		}
1953
1954
		return false;
1955
	}
1956
1957
	/**
1958
	 * Check if the page can be cached
1959
	 * @return bool
1960
	 */
1961
	public function isFileCacheable() {
1962
		$cacheable = false;
1963
1964
		if ( HTMLFileCache::useFileCache( $this->getContext() ) ) {
1965
			$cacheable = $this->mPage->getId()
1966
				&& !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
1967
			// Extension may have reason to disable file caching on some pages.
1968
			if ( $cacheable ) {
1969
				$cacheable = Hooks::run( 'IsFileCacheable', [ &$this ] );
1970
			}
1971
		}
1972
1973
		return $cacheable;
1974
	}
1975
1976
	/**#@-*/
1977
1978
	/**
1979
	 * Lightweight method to get the parser output for a page, checking the parser cache
1980
	 * and so on. Doesn't consider most of the stuff that WikiPage::view is forced to
1981
	 * consider, so it's not appropriate to use there.
1982
	 *
1983
	 * @since 1.16 (r52326) for LiquidThreads
1984
	 *
1985
	 * @param int|null $oldid Revision ID or null
1986
	 * @param User $user The relevant user
1987
	 * @return ParserOutput|bool ParserOutput or false if the given revision ID is not found
1988
	 */
1989
	public function getParserOutput( $oldid = null, User $user = null ) {
1990
		// XXX: bypasses mParserOptions and thus setParserOptions()
1991
1992
		if ( $user === null ) {
1993
			$parserOptions = $this->getParserOptions();
1994
		} else {
1995
			$parserOptions = $this->mPage->makeParserOptions( $user );
1996
		}
1997
1998
		return $this->mPage->getParserOutput( $parserOptions, $oldid );
1999
	}
2000
2001
	/**
2002
	 * Override the ParserOptions used to render the primary article wikitext.
2003
	 *
2004
	 * @param ParserOptions $options
2005
	 * @throws MWException If the parser options where already initialized.
2006
	 */
2007
	public function setParserOptions( ParserOptions $options ) {
2008
		if ( $this->mParserOptions ) {
2009
			throw new MWException( "can't change parser options after they have already been set" );
2010
		}
2011
2012
		// clone, so if $options is modified later, it doesn't confuse the parser cache.
2013
		$this->mParserOptions = clone $options;
2014
	}
2015
2016
	/**
2017
	 * Get parser options suitable for rendering the primary article wikitext
2018
	 * @return ParserOptions
2019
	 */
2020
	public function getParserOptions() {
2021
		if ( !$this->mParserOptions ) {
2022
			$this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2023
		}
2024
		// Clone to allow modifications of the return value without affecting cache
2025
		return clone $this->mParserOptions;
2026
	}
2027
2028
	/**
2029
	 * Sets the context this Article is executed in
2030
	 *
2031
	 * @param IContextSource $context
2032
	 * @since 1.18
2033
	 */
2034
	public function setContext( $context ) {
2035
		$this->mContext = $context;
2036
	}
2037
2038
	/**
2039
	 * Gets the context this Article is executed in
2040
	 *
2041
	 * @return IContextSource
2042
	 * @since 1.18
2043
	 */
2044 View Code Duplication
	public function getContext() {
2045
		if ( $this->mContext instanceof IContextSource ) {
2046
			return $this->mContext;
2047
		} else {
2048
			wfDebug( __METHOD__ . " called and \$mContext is null. " .
2049
				"Return RequestContext::getMain(); for sanity\n" );
2050
			return RequestContext::getMain();
2051
		}
2052
	}
2053
2054
	/**
2055
	 * Use PHP's magic __get handler to handle accessing of
2056
	 * raw WikiPage fields for backwards compatibility.
2057
	 *
2058
	 * @param string $fname Field name
2059
	 * @return mixed
2060
	 */
2061
	public function __get( $fname ) {
2062
		if ( property_exists( $this->mPage, $fname ) ) {
2063
			# wfWarn( "Access to raw $fname field " . __CLASS__ );
2064
			return $this->mPage->$fname;
2065
		}
2066
		trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2067
	}
2068
2069
	/**
2070
	 * Use PHP's magic __set handler to handle setting of
2071
	 * raw WikiPage fields for backwards compatibility.
2072
	 *
2073
	 * @param string $fname Field name
2074
	 * @param mixed $fvalue New value
2075
	 */
2076
	public function __set( $fname, $fvalue ) {
2077
		if ( property_exists( $this->mPage, $fname ) ) {
2078
			# wfWarn( "Access to raw $fname field of " . __CLASS__ );
2079
			$this->mPage->$fname = $fvalue;
2080
		// Note: extensions may want to toss on new fields
2081
		} elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2082
			$this->mPage->$fname = $fvalue;
2083
		} else {
2084
			trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2085
		}
2086
	}
2087
2088
	/**
2089
	 * Call to WikiPage function for backwards compatibility.
2090
	 * @see WikiPage::checkFlags
2091
	 */
2092
	public function checkFlags( $flags ) {
2093
		return $this->mPage->checkFlags( $flags );
2094
	}
2095
2096
	/**
2097
	 * Call to WikiPage function for backwards compatibility.
2098
	 * @see WikiPage::checkTouched
2099
	 */
2100
	public function checkTouched() {
2101
		return $this->mPage->checkTouched();
2102
	}
2103
2104
	/**
2105
	 * Call to WikiPage function for backwards compatibility.
2106
	 * @see WikiPage::clearPreparedEdit
2107
	 */
2108
	public function clearPreparedEdit() {
2109
		$this->mPage->clearPreparedEdit();
2110
	}
2111
2112
	/**
2113
	 * Call to WikiPage function for backwards compatibility.
2114
	 * @see WikiPage::doDeleteArticleReal
2115
	 */
2116
	public function doDeleteArticleReal(
2117
		$reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null
2118
	) {
2119
		return $this->mPage->doDeleteArticleReal(
2120
			$reason, $suppress, $u1, $u2, $error, $user
2121
		);
2122
	}
2123
2124
	/**
2125
	 * Call to WikiPage function for backwards compatibility.
2126
	 * @see WikiPage::doDeleteUpdates
2127
	 */
2128
	public function doDeleteUpdates( $id, Content $content = null ) {
2129
		return $this->mPage->doDeleteUpdates( $id, $content );
2130
	}
2131
2132
	/**
2133
	 * Call to WikiPage function for backwards compatibility.
2134
	 * @see WikiPage::doEdit
2135
	 */
2136
	public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
2137
		ContentHandler::deprecated( __METHOD__, '1.21' );
2138
		return $this->mPage->doEdit( $text, $summary, $flags, $baseRevId, $user );
0 ignored issues
show
Deprecated Code introduced by
The method WikiPage::doEdit() has been deprecated with message: since 1.21: use doEditContent() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
2139
	}
2140
2141
	/**
2142
	 * Call to WikiPage function for backwards compatibility.
2143
	 * @see WikiPage::doEditContent
2144
	 */
2145
	public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
2146
		User $user = null, $serialFormat = null
2147
	) {
2148
		return $this->mPage->doEditContent( $content, $summary, $flags, $baseRevId,
2149
			$user, $serialFormat
2150
		);
2151
	}
2152
2153
	/**
2154
	 * Call to WikiPage function for backwards compatibility.
2155
	 * @see WikiPage::doEditUpdates
2156
	 */
2157
	public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2158
		return $this->mPage->doEditUpdates( $revision, $user, $options );
2159
	}
2160
2161
	/**
2162
	 * Call to WikiPage function for backwards compatibility.
2163
	 * @see WikiPage::doPurge
2164
	 */
2165
	public function doPurge() {
2166
		return $this->mPage->doPurge();
2167
	}
2168
2169
	/**
2170
	 * Call to WikiPage function for backwards compatibility.
2171
	 * @see WikiPage::doQuickEditContent
2172
	 */
2173
	public function doQuickEditContent(
2174
		Content $content, User $user, $comment = '', $minor = false, $serialFormat = null
2175
	) {
2176
		return $this->mPage->doQuickEditContent(
2177
			$content, $user, $comment, $minor, $serialFormat
2178
		);
2179
	}
2180
2181
	/**
2182
	 * Call to WikiPage function for backwards compatibility.
2183
	 * @see WikiPage::doViewUpdates
2184
	 */
2185
	public function doViewUpdates( User $user, $oldid = 0 ) {
2186
		$this->mPage->doViewUpdates( $user, $oldid );
2187
	}
2188
2189
	/**
2190
	 * Call to WikiPage function for backwards compatibility.
2191
	 * @see WikiPage::exists
2192
	 */
2193
	public function exists() {
2194
		return $this->mPage->exists();
2195
	}
2196
2197
	/**
2198
	 * Call to WikiPage function for backwards compatibility.
2199
	 * @see WikiPage::followRedirect
2200
	 */
2201
	public function followRedirect() {
2202
		return $this->mPage->followRedirect();
2203
	}
2204
2205
	/**
2206
	 * Call to WikiPage function for backwards compatibility.
2207
	 * @see WikiPage::getActionOverrides
2208
	 */
2209
	public function getActionOverrides() {
2210
		return $this->mPage->getActionOverrides();
2211
	}
2212
2213
	/**
2214
	 * Call to WikiPage function for backwards compatibility.
2215
	 * @see WikiPage::getAutoDeleteReason
2216
	 */
2217
	public function getAutoDeleteReason( &$hasHistory ) {
2218
		return $this->mPage->getAutoDeleteReason( $hasHistory );
2219
	}
2220
2221
	/**
2222
	 * Call to WikiPage function for backwards compatibility.
2223
	 * @see WikiPage::getCategories
2224
	 */
2225
	public function getCategories() {
2226
		return $this->mPage->getCategories();
2227
	}
2228
2229
	/**
2230
	 * Call to WikiPage function for backwards compatibility.
2231
	 * @see WikiPage::getComment
2232
	 */
2233
	public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2234
		return $this->mPage->getComment( $audience, $user );
2235
	}
2236
2237
	/**
2238
	 * Call to WikiPage function for backwards compatibility.
2239
	 * @see WikiPage::getContentHandler
2240
	 */
2241
	public function getContentHandler() {
2242
		return $this->mPage->getContentHandler();
2243
	}
2244
2245
	/**
2246
	 * Call to WikiPage function for backwards compatibility.
2247
	 * @see WikiPage::getContentModel
2248
	 */
2249
	public function getContentModel() {
2250
		return $this->mPage->getContentModel();
2251
	}
2252
2253
	/**
2254
	 * Call to WikiPage function for backwards compatibility.
2255
	 * @see WikiPage::getContributors
2256
	 */
2257
	public function getContributors() {
2258
		return $this->mPage->getContributors();
2259
	}
2260
2261
	/**
2262
	 * Call to WikiPage function for backwards compatibility.
2263
	 * @see WikiPage::getCreator
2264
	 */
2265
	public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2266
		return $this->mPage->getCreator( $audience, $user );
2267
	}
2268
2269
	/**
2270
	 * Call to WikiPage function for backwards compatibility.
2271
	 * @see WikiPage::getDeletionUpdates
2272
	 */
2273
	public function getDeletionUpdates( Content $content = null ) {
2274
		return $this->mPage->getDeletionUpdates( $content );
2275
	}
2276
2277
	/**
2278
	 * Call to WikiPage function for backwards compatibility.
2279
	 * @see WikiPage::getHiddenCategories
2280
	 */
2281
	public function getHiddenCategories() {
2282
		return $this->mPage->getHiddenCategories();
2283
	}
2284
2285
	/**
2286
	 * Call to WikiPage function for backwards compatibility.
2287
	 * @see WikiPage::getId
2288
	 */
2289
	public function getId() {
2290
		return $this->mPage->getId();
2291
	}
2292
2293
	/**
2294
	 * Call to WikiPage function for backwards compatibility.
2295
	 * @see WikiPage::getLatest
2296
	 */
2297
	public function getLatest() {
2298
		return $this->mPage->getLatest();
2299
	}
2300
2301
	/**
2302
	 * Call to WikiPage function for backwards compatibility.
2303
	 * @see WikiPage::getLinksTimestamp
2304
	 */
2305
	public function getLinksTimestamp() {
2306
		return $this->mPage->getLinksTimestamp();
2307
	}
2308
2309
	/**
2310
	 * Call to WikiPage function for backwards compatibility.
2311
	 * @see WikiPage::getMinorEdit
2312
	 */
2313
	public function getMinorEdit() {
2314
		return $this->mPage->getMinorEdit();
2315
	}
2316
2317
	/**
2318
	 * Call to WikiPage function for backwards compatibility.
2319
	 * @see WikiPage::getOldestRevision
2320
	 */
2321
	public function getOldestRevision() {
2322
		return $this->mPage->getOldestRevision();
2323
	}
2324
2325
	/**
2326
	 * Call to WikiPage function for backwards compatibility.
2327
	 * @see WikiPage::getRedirectTarget
2328
	 */
2329
	public function getRedirectTarget() {
2330
		return $this->mPage->getRedirectTarget();
2331
	}
2332
2333
	/**
2334
	 * Call to WikiPage function for backwards compatibility.
2335
	 * @see WikiPage::getRedirectURL
2336
	 */
2337
	public function getRedirectURL( $rt ) {
2338
		return $this->mPage->getRedirectURL( $rt );
2339
	}
2340
2341
	/**
2342
	 * Call to WikiPage function for backwards compatibility.
2343
	 * @see WikiPage::getRevision
2344
	 */
2345
	public function getRevision() {
2346
		return $this->mPage->getRevision();
2347
	}
2348
2349
	/**
2350
	 * Call to WikiPage function for backwards compatibility.
2351
	 * @see WikiPage::getText
2352
	 */
2353
	public function getText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2354
		ContentHandler::deprecated( __METHOD__, '1.21' );
2355
		return $this->mPage->getText( $audience, $user );
0 ignored issues
show
Deprecated Code introduced by
The method WikiPage::getText() has been deprecated with message: since 1.21, getContent() should be used instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
2356
	}
2357
2358
	/**
2359
	 * Call to WikiPage function for backwards compatibility.
2360
	 * @see WikiPage::getTimestamp
2361
	 */
2362
	public function getTimestamp() {
2363
		return $this->mPage->getTimestamp();
2364
	}
2365
2366
	/**
2367
	 * Call to WikiPage function for backwards compatibility.
2368
	 * @see WikiPage::getTouched
2369
	 */
2370
	public function getTouched() {
2371
		return $this->mPage->getTouched();
2372
	}
2373
2374
	/**
2375
	 * Call to WikiPage function for backwards compatibility.
2376
	 * @see WikiPage::getUndoContent
2377
	 */
2378
	public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2379
		return $this->mPage->getUndoContent( $undo, $undoafter );
2380
	}
2381
2382
	/**
2383
	 * Call to WikiPage function for backwards compatibility.
2384
	 * @see WikiPage::getUser
2385
	 */
2386
	public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2387
		return $this->mPage->getUser( $audience, $user );
2388
	}
2389
2390
	/**
2391
	 * Call to WikiPage function for backwards compatibility.
2392
	 * @see WikiPage::getUserText
2393
	 */
2394
	public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2395
		return $this->mPage->getUserText( $audience, $user );
2396
	}
2397
2398
	/**
2399
	 * Call to WikiPage function for backwards compatibility.
2400
	 * @see WikiPage::hasViewableContent
2401
	 */
2402
	public function hasViewableContent() {
2403
		return $this->mPage->hasViewableContent();
2404
	}
2405
2406
	/**
2407
	 * Call to WikiPage function for backwards compatibility.
2408
	 * @see WikiPage::insertOn
2409
	 */
2410
	public function insertOn( $dbw, $pageId = null ) {
2411
		return $this->mPage->insertOn( $dbw, $pageId );
2412
	}
2413
2414
	/**
2415
	 * Call to WikiPage function for backwards compatibility.
2416
	 * @see WikiPage::insertProtectNullRevision
2417
	 */
2418
	public function insertProtectNullRevision( $revCommentMsg, array $limit,
2419
		array $expiry, $cascade, $reason, $user = null
2420
	) {
2421
		return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2422
			$expiry, $cascade, $reason, $user
2423
		);
2424
	}
2425
2426
	/**
2427
	 * Call to WikiPage function for backwards compatibility.
2428
	 * @see WikiPage::insertRedirect
2429
	 */
2430
	public function insertRedirect() {
2431
		return $this->mPage->insertRedirect();
2432
	}
2433
2434
	/**
2435
	 * Call to WikiPage function for backwards compatibility.
2436
	 * @see WikiPage::insertRedirectEntry
2437
	 */
2438
	public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2439
		return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2440
	}
2441
2442
	/**
2443
	 * Call to WikiPage function for backwards compatibility.
2444
	 * @see WikiPage::isCountable
2445
	 */
2446
	public function isCountable( $editInfo = false ) {
2447
		return $this->mPage->isCountable( $editInfo );
2448
	}
2449
2450
	/**
2451
	 * Call to WikiPage function for backwards compatibility.
2452
	 * @see WikiPage::isRedirect
2453
	 */
2454
	public function isRedirect() {
2455
		return $this->mPage->isRedirect();
2456
	}
2457
2458
	/**
2459
	 * Call to WikiPage function for backwards compatibility.
2460
	 * @see WikiPage::loadFromRow
2461
	 */
2462
	public function loadFromRow( $data, $from ) {
2463
		return $this->mPage->loadFromRow( $data, $from );
2464
	}
2465
2466
	/**
2467
	 * Call to WikiPage function for backwards compatibility.
2468
	 * @see WikiPage::loadPageData
2469
	 */
2470
	public function loadPageData( $from = 'fromdb' ) {
2471
		$this->mPage->loadPageData( $from );
2472
	}
2473
2474
	/**
2475
	 * Call to WikiPage function for backwards compatibility.
2476
	 * @see WikiPage::lockAndGetLatest
2477
	 */
2478
	public function lockAndGetLatest() {
2479
		return $this->mPage->lockAndGetLatest();
2480
	}
2481
2482
	/**
2483
	 * Call to WikiPage function for backwards compatibility.
2484
	 * @see WikiPage::makeParserOptions
2485
	 */
2486
	public function makeParserOptions( $context ) {
2487
		return $this->mPage->makeParserOptions( $context );
2488
	}
2489
2490
	/**
2491
	 * Call to WikiPage function for backwards compatibility.
2492
	 * @see WikiPage::pageDataFromId
2493
	 */
2494
	public function pageDataFromId( $dbr, $id, $options = [] ) {
2495
		return $this->mPage->pageDataFromId( $dbr, $id, $options );
2496
	}
2497
2498
	/**
2499
	 * Call to WikiPage function for backwards compatibility.
2500
	 * @see WikiPage::pageDataFromTitle
2501
	 */
2502
	public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2503
		return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2504
	}
2505
2506
	/**
2507
	 * Call to WikiPage function for backwards compatibility.
2508
	 * @see WikiPage::prepareContentForEdit
2509
	 */
2510
	public function prepareContentForEdit(
2511
		Content $content, $revision = null, User $user = null,
2512
		$serialFormat = null, $useCache = true
2513
	) {
2514
		return $this->mPage->prepareContentForEdit(
2515
			$content, $revision, $user,
2516
			$serialFormat, $useCache
2517
		);
2518
	}
2519
2520
	/**
2521
	 * Call to WikiPage function for backwards compatibility.
2522
	 * @see WikiPage::prepareTextForEdit
2523
	 */
2524
	public function prepareTextForEdit( $text, $revid = null, User $user = null ) {
2525
		return $this->mPage->prepareTextForEdit( $text, $revid, $user );
0 ignored issues
show
Deprecated Code introduced by
The method WikiPage::prepareTextForEdit() has been deprecated with message: since 1.21: use prepareContentForEdit instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
2526
	}
2527
2528
	/**
2529
	 * Call to WikiPage function for backwards compatibility.
2530
	 * @see WikiPage::protectDescription
2531
	 */
2532
	public function protectDescription( array $limit, array $expiry ) {
2533
		return $this->mPage->protectDescription( $limit, $expiry );
2534
	}
2535
2536
	/**
2537
	 * Call to WikiPage function for backwards compatibility.
2538
	 * @see WikiPage::protectDescriptionLog
2539
	 */
2540
	public function protectDescriptionLog( array $limit, array $expiry ) {
2541
		return $this->mPage->protectDescriptionLog( $limit, $expiry );
2542
	}
2543
2544
	/**
2545
	 * Call to WikiPage function for backwards compatibility.
2546
	 * @see WikiPage::replaceSectionAtRev
2547
	 */
2548
	public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2549
		$sectionTitle = '', $baseRevId = null
2550
	) {
2551
		return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2552
			$sectionTitle, $baseRevId
2553
		);
2554
	}
2555
2556
	/**
2557
	 * Call to WikiPage function for backwards compatibility.
2558
	 * @see WikiPage::replaceSectionContent
2559
	 */
2560
	public function replaceSectionContent(
2561
		$sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2562
	) {
2563
		return $this->mPage->replaceSectionContent(
0 ignored issues
show
Deprecated Code introduced by
The method WikiPage::replaceSectionContent() has been deprecated with message: since 1.24, use replaceSectionAtRev instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
2564
			$sectionId, $sectionContent, $sectionTitle, $edittime
2565
		);
2566
	}
2567
2568
	/**
2569
	 * Call to WikiPage function for backwards compatibility.
2570
	 * @see WikiPage::setTimestamp
2571
	 */
2572
	public function setTimestamp( $ts ) {
2573
		return $this->mPage->setTimestamp( $ts );
2574
	}
2575
2576
	/**
2577
	 * Call to WikiPage function for backwards compatibility.
2578
	 * @see WikiPage::shouldCheckParserCache
2579
	 */
2580
	public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2581
		return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2582
	}
2583
2584
	/**
2585
	 * Call to WikiPage function for backwards compatibility.
2586
	 * @see WikiPage::supportsSections
2587
	 */
2588
	public function supportsSections() {
2589
		return $this->mPage->supportsSections();
2590
	}
2591
2592
	/**
2593
	 * Call to WikiPage function for backwards compatibility.
2594
	 * @see WikiPage::triggerOpportunisticLinksUpdate
2595
	 */
2596
	public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) {
2597
		return $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2598
	}
2599
2600
	/**
2601
	 * Call to WikiPage function for backwards compatibility.
2602
	 * @see WikiPage::updateCategoryCounts
2603
	 */
2604
	public function updateCategoryCounts( array $added, array $deleted ) {
2605
		return $this->mPage->updateCategoryCounts( $added, $deleted );
2606
	}
2607
2608
	/**
2609
	 * Call to WikiPage function for backwards compatibility.
2610
	 * @see WikiPage::updateIfNewerOn
2611
	 */
2612
	public function updateIfNewerOn( $dbw, $revision ) {
2613
		return $this->mPage->updateIfNewerOn( $dbw, $revision );
0 ignored issues
show
Deprecated Code introduced by
The method WikiPage::updateIfNewerOn() has been deprecated with message: since 1.24, use updateRevisionOn instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
2614
	}
2615
2616
	/**
2617
	 * Call to WikiPage function for backwards compatibility.
2618
	 * @see WikiPage::updateRedirectOn
2619
	 */
2620
	public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2621
		return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null );
2622
	}
2623
2624
	/**
2625
	 * Call to WikiPage function for backwards compatibility.
2626
	 * @see WikiPage::updateRevisionOn
2627
	 */
2628
	public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
2629
		$lastRevIsRedirect = null
2630
	) {
2631
		return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
2632
			$lastRevIsRedirect
2633
		);
2634
	}
2635
2636
	/**
2637
	 * @param array $limit
2638
	 * @param array $expiry
2639
	 * @param bool $cascade
2640
	 * @param string $reason
2641
	 * @param User $user
2642
	 * @return Status
2643
	 */
2644
	public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
2645
		$reason, User $user
2646
	) {
2647
		return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
2648
	}
2649
2650
	/**
2651
	 * @param array $limit
2652
	 * @param string $reason
2653
	 * @param int $cascade
2654
	 * @param array $expiry
2655
	 * @return bool
2656
	 */
2657
	public function updateRestrictions( $limit = [], $reason = '',
2658
		&$cascade = 0, $expiry = []
2659
	) {
2660
		return $this->mPage->doUpdateRestrictions(
2661
			$limit,
2662
			$expiry,
2663
			$cascade,
2664
			$reason,
2665
			$this->getContext()->getUser()
2666
		);
2667
	}
2668
2669
	/**
2670
	 * @param string $reason
2671
	 * @param bool $suppress
2672
	 * @param int $u1 Unused
2673
	 * @param bool $u2 Unused
2674
	 * @param string $error
2675
	 * @return bool
2676
	 */
2677
	public function doDeleteArticle(
2678
		$reason, $suppress = false, $u1 = null, $u2 = null, &$error = ''
2679
	) {
2680
		return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error );
2681
	}
2682
2683
	/**
2684
	 * @param string $fromP
2685
	 * @param string $summary
2686
	 * @param string $token
2687
	 * @param bool $bot
2688
	 * @param array $resultDetails
2689
	 * @param User|null $user
2690
	 * @return array
2691
	 */
2692
	public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
2693
		$user = is_null( $user ) ? $this->getContext()->getUser() : $user;
2694
		return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
2695
	}
2696
2697
	/**
2698
	 * @param string $fromP
2699
	 * @param string $summary
2700
	 * @param bool $bot
2701
	 * @param array $resultDetails
2702
	 * @param User|null $guser
2703
	 * @return array
2704
	 */
2705
	public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
2706
		$guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
2707
		return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
2708
	}
2709
2710
	/**
2711
	 * @param bool $hasHistory
2712
	 * @return mixed
2713
	 */
2714
	public function generateReason( &$hasHistory ) {
2715
		$title = $this->mPage->getTitle();
2716
		$handler = ContentHandler::getForTitle( $title );
2717
		return $handler->getAutoDeleteReason( $title, $hasHistory );
2718
	}
2719
2720
	/**
2721
	 * @return array
2722
	 *
2723
	 * @deprecated since 1.24, use WikiPage::selectFields() instead
2724
	 */
2725
	public static function selectFields() {
2726
		wfDeprecated( __METHOD__, '1.24' );
2727
		return WikiPage::selectFields();
2728
	}
2729
2730
	/**
2731
	 * @param Title $title
2732
	 *
2733
	 * @deprecated since 1.24, use WikiPage::onArticleCreate() instead
2734
	 */
2735
	public static function onArticleCreate( $title ) {
2736
		wfDeprecated( __METHOD__, '1.24' );
2737
		WikiPage::onArticleCreate( $title );
2738
	}
2739
2740
	/**
2741
	 * @param Title $title
2742
	 *
2743
	 * @deprecated since 1.24, use WikiPage::onArticleDelete() instead
2744
	 */
2745
	public static function onArticleDelete( $title ) {
2746
		wfDeprecated( __METHOD__, '1.24' );
2747
		WikiPage::onArticleDelete( $title );
2748
	}
2749
2750
	/**
2751
	 * @param Title $title
2752
	 *
2753
	 * @deprecated since 1.24, use WikiPage::onArticleEdit() instead
2754
	 */
2755
	public static function onArticleEdit( $title ) {
2756
		wfDeprecated( __METHOD__, '1.24' );
2757
		WikiPage::onArticleEdit( $title );
2758
	}
2759
2760
	/**
2761
	 * @param string $oldtext
2762
	 * @param string $newtext
2763
	 * @param int $flags
2764
	 * @return string
2765
	 * @deprecated since 1.21, use ContentHandler::getAutosummary() instead
2766
	 */
2767
	public static function getAutosummary( $oldtext, $newtext, $flags ) {
2768
		return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
0 ignored issues
show
Deprecated Code introduced by
The method WikiPage::getAutosummary() has been deprecated with message: since 1.21, use ContentHandler::getAutosummary() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
2769
	}
2770
	// ******
2771
}
2772