Index: includes/SpecialWhatlinkshere.php
===================================================================
--- includes/SpecialWhatlinkshere.php	(revision 23311)
+++ includes/SpecialWhatlinkshere.php	(working copy)
@@ -24,8 +24,18 @@
 	var $selfTitle, $skin;
 
 	private $namespace;
+	
+	/**
+	 * An array specifying which of links, inclusions, redirects to display.
+	 * Format is self-explanatory.
+	 */
+	private $mShow = array(
+		'link' => true,
+		'inclusion' => true,
+		'redirect' => true
+	);
 
-	function WhatLinksHerePage( &$request, $par = null ) {
+	function __construct( &$request, $par = null ) {
 		global $wgUser;
 		$this->request =& $request;
 		$this->skin = $wgUser->getSkin();
@@ -41,6 +51,14 @@
 		}
 		$this->from = $this->request->getInt( 'from' );
 		$this->back = $this->request->getInt( 'back' );
+		
+		// Ideally we'd have checkboxes saying something like "show X", not
+		// "hide X", but due to the vagaries of HTML, that would cause issues
+		// if the user visited the page without checking anything (say by
+		// direct link), since then we would have to assume they're false.
+		foreach( array_keys( $this->mShow ) as $key ) {
+			$this->mShow[$key] = !$this->request->getCheck( "hide$key" );
+		}
 
 		$targetString = isset($this->par) ? $this->par : $this->request->getVal( 'target' );
 
@@ -74,9 +92,7 @@
 	 */
 	function showIndirectLinks( $level, $target, $limit, $from = 0, $back = 0 ) {
 		global $wgOut;
-		$fname = 'WhatLinksHerePage::showIndirectLinks';
 		$dbr = wfGetDB( DB_READ );
-		$options = array();
 
 		$ns = $this->request->getIntOrNull( 'namespace' );
 		if ( isset( $ns ) ) {
@@ -86,49 +102,67 @@
 			$options['namespace'] = '';
 		}
 
-		// Make the query
-		$plConds = array(
-			'page_id=pl_from',
-			'pl_namespace' => $target->getNamespace(),
-			'pl_title' => $target->getDBkey(),
-		);
+		// Make sure options are sane
+		if( !$this->mShow['link'] and !$this->mShow['inclusion']
+		and !$this->mShow['redirect'] ) {
+			// Uh, why are you viewing the page in the first place?
+			$this->mShow['link'] = $this->mShow['inclusion'] =
+			$this->mShow['redirect'] = true;
+		}
 
-		$tlConds = array(
-			'page_id=tl_from',
-			'tl_namespace' => $target->getNamespace(),
-			'tl_title' => $target->getDBkey(),
-		);
+		// Make the queries.
+		// templatelinks comes second so that the templatelinks row overwrites
+		// the pagelinks row, so we get (inclusion) rather than nothing when we
+		// merge the two later.
+		$queries = array();
 
-		if ( $this->namespace !== null ){
-			$plConds['page_namespace'] = (int)$this->namespace;
-			$tlConds['page_namespace'] = (int)$this->namespace;
+		if( $this->mShow['link'] or $this->mShow['redirect'] ) {
+			$queries['page'] = 'pl_';
 		}
+		if( $this->mShow['inclusion'] ) {
+			$queries['template'] = 'tl_';
+		}
 
-		if ( $from ) {
-			$from = (int)$from; // just in case
-			$tlConds[] = "tl_from >= $from";
-			$plConds[] = "pl_from >= $from";
-		} 
+		foreach( $queries as $linksType => $tablePrefix ) {
+			$options = array();
+			$conds = array(
+					"page_id={$tablePrefix}from",
+					"{$tablePrefix}namespace" => $target->getNamespace(),
+					"{$tablePrefix}title" => $target->getDBkey(),
+			);
+			// Read an extra row as an at-end check
+			$options['limit'] = $limit + 1;
+			// enforce join order, sometimes namespace selector may trigger
+			// filesorts which are far less efficient than scanning many
+			// entries
+			$options[] = 'STRAIGHT_JOIN';
+			$options['ORDER BY'] = "{$tablePrefix}from";
 
-		// Read an extra row as an at-end check
-		$queryLimit = $limit + 1;
-		
-		// enforce join order, sometimes namespace selector may 
-		// trigger filesorts which are far less efficient than scanning many entries
-		$options[] = 'STRAIGHT_JOIN';
-		
-		$options['LIMIT'] = $queryLimit;
-		$fields = array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' );
+			if ( $this->namespace !== null ){
+				$conds['page_namespace'] = (int)$this->namespace;
+			}
+			if ( $from ) {
+				$from = (int)$from; // just in case
+				$query['conds'][] = "{$tablePrefix}from >= $from";
+			}
+			if( !$this->mShow['redirect'] ) {
+				$conds[] = 'page_is_redirect != 1';
+			}
 
-		$options['ORDER BY'] = 'pl_from';
-		$plRes = $dbr->select( array( 'pagelinks', 'page' ), $fields,
-			$plConds, $fname, $options );
-			
-		$options['ORDER BY'] = 'tl_from';
-		$tlRes = $dbr->select( array( 'templatelinks', 'page' ), $fields,
-			$tlConds, $fname, $options );
-		
-		if ( !$dbr->numRows( $plRes ) && !$dbr->numRows( $tlRes ) ) {
+			$fields = array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' );
+			$res = $dbr->select( array( "{$linksType}links", 'page' ), $fields, $conds, __METHOD__, $options );
+
+			// Read the rows into an array and remove duplicates.  This is
+			// where page before template matters!
+			while ( $row = $dbr->fetchObject( $res ) ) {
+				$row->is_template = $linksType == 'template';
+				$rows[$row->page_id] = $row;
+			}
+			$dbr->freeResult( $res );
+		}
+
+		// Are there any links at all?  If not, abort.
+		if ( !count($rows) ) {
 			if ( 0 == $level && !isset( $this->namespace ) ) {
 				// really no links to here
 				$wgOut->addWikiText( wfMsg( 'nolinkshere', $this->target->getPrefixedText() ) );
@@ -144,35 +178,6 @@
 			return;
 		}
 
-		$options = array();
-		list( $options['limit'], $options['offset']) = wfCheckLimits();
-		if ( ( $ns = $this->request->getVal( 'namespace', null ) ) !== null && $ns !== '' && ctype_digit($ns) ) {
-			$options['namespace'] = intval( $ns );
-			$this->setNamespace( $options['namespace'] );
-		} else {
-			$options['namespace'] = '';
-			$this->setNamespace( null );
-		}
-		$options['offset'] = $this->request->getVal( 'offset' );
-		/* Offset must be an integral. */
-		if ( !strlen( $options['offset'] ) || !preg_match( '/^[0-9]+$/', $options['offset'] ) )
-		$options['offset'] = '';
-		$options['target'] = $this->target->getPrefixedDBkey();
-
-		// Read the rows into an array and remove duplicates
-		// templatelinks comes second so that the templatelinks row overwrites the
-		// pagelinks row, so we get (inclusion) rather than nothing
-		while ( $row = $dbr->fetchObject( $plRes ) ) {
-			$row->is_template = 0;
-			$rows[$row->page_id] = $row;
-		}
-		$dbr->freeResult( $plRes );
-		while ( $row = $dbr->fetchObject( $tlRes ) ) {
-			$row->is_template = 1;
-			$rows[$row->page_id] = $row;
-		}
-		$dbr->freeResult( $tlRes );
-
 		// Sort by key and then change the keys to 0-based indices
 		ksort( $rows );
 		$rows = array_values( $rows );
@@ -192,6 +197,21 @@
 		}
 		$prevId = $from;
 
+		// Set up options for the new Whatlinkshere form to display with the
+		// results
+		$options = array();
+		list( $options['limit'], $options['offset']) = wfCheckLimits();
+		if ( ( $ns = $this->request->getIntOrNull( 'namespace' ) ) !== null ) {
+			$options['namespace'] = $ns;
+			$this->setNamespace( $options['namespace'] );
+		} else {
+			$options['namespace'] = '';
+			$this->setNamespace( null );
+		}
+		$options['offset'] = $this->request->getInt( 'offset' );
+		$options['target'] = $this->target->getPrefixedDBkey();
+		// End options setup
+
 		if ( $level == 0 ) {
 			$wgOut->addHTML( $this->whatlinkshereForm( $options ) );
 			$wgOut->addWikiText( wfMsg( 'linkshere', $this->target->getPrefixedText() ) );
