/**
	 * Execute the subpage.
	 * @param $params array Array of subpage parameters.
	 */
	function execute( $params ) {
		global $wgOut, $wgUser, $wgLang;

		if ( !count( $params ) ) {
			$wgOut->addWikiMsg( 'securepoll-too-few-params' );
			return;
		}
		
		$this->voteId = intval( $params[0] );

		$db = $this->context->getDB();
		$row = $db->selectRow( 
			array( 'securepoll_votes', 'securepoll_elections', 'securepoll_voters' ),
			'*',
			array(
				'vote_id' => $this->voteId,
				'vote_election=el_entity',
				'vote_voter=voter_id',
			),
			__METHOD__
		);
		if ( !$row ) {
			$wgOut->addWikiMsg( 'securepoll-invalid-vote', $this->voteId );
			return;
		}

		$this->election = $this->context->newElectionFromRow( $row );
		$this->initLanguage( $wgUser, $this->election );

		$this->parent->setSubtitle( array( 
			$this->parent->getTitle( 'list/' . $this->election->getId() ), 
			wfMsg( 'securepoll-list-title', $this->election->getMessage( 'title' ) ) ) );

		if ( !$this->election->isAdmin( $wgUser ) ) {
			$wgOut->addWikiMsg( 'securepoll-need-admin' );
			return;
		}
		# Show vote properties
		$wgOut->setPageTitle( wfMsg( 
			'securepoll-details-title', $this->voteId ) );

		$wgOut->addHTML(
			'<table class="mw-datatable TablePager">' .
			$this->detailEntry( 'securepoll-header-id', $row->vote_id ) .
			$this->detailEntry( 'securepoll-header-timestamp', $row->vote_timestamp ) .
			$this->detailEntry( 'securepoll-header-voter-name', $row->voter_name ) .
			$this->detailEntry( 'securepoll-header-voter-type', $row->voter_type ) .
			$this->detailEntry( 'securepoll-header-voter-domain', $row->voter_domain ) .
			$this->detailEntry( 'securepoll-header-url', $row->voter_url ) .
			$this->detailEntry( 'securepoll-header-ip', IP::formatHex( $row->vote_ip ) ) .
			$this->detailEntry( 'securepoll-header-xff', $row->vote_xff ) .
			$this->detailEntry( 'securepoll-header-ua', $row->vote_ua ) .
			$this->detailEntry( 'securepoll-header-token-match', $row->vote_token_match ) .
			'</table>'
		);

		# Show voter properties
		$wgOut->addHTML( '<h2>' . wfMsgHTML( 'securepoll-voter-properties' ) . "</h2>\n" );
		$wgOut->addHTML( '<table class="mw-datatable TablePager">' );
		$props = SecurePoll_Voter::decodeProperties( $row->voter_properties );
		foreach ( $props as $name => $value ) {
			if ( is_array( $value ) ) {
				$value = implode( ', ', $value );
			}
			$wgOut->addHTML( 
				'<td class="securepoll-detail-header">' .
				htmlspecialchars( $name ) . "</td>\n" .
				'<td>' . htmlspecialchars( $value ) . "</td></tr>\n"
			);
		}
		$wgOut->addHTML( '</table>' );

		# Show cookie dups
		$cmTable = $db->tableName( 'securepoll_cookie_match' );
		$voterId = intval( $row->voter_id );
		$sql = "(SELECT cm_voter_2 as voter, cm_timestamp FROM $cmTable WHERE cm_voter_1=$voterId)" .
			" UNION " . 
			"(SELECT cm_voter_1 as voter, cm_timestamp FROM $cmTable WHERE cm_voter_2=$voterId)";
		$res = $db->query( $sql, __METHOD__ );
		if ( $res->numRows() ) {
			$wgOut->addHTML( '<h2>' . wfMsgHTML( 'securepoll-cookie-dup-list' ) . '</h2>' );
			$wgOut->addHTML( '<table class="mw-datatable TablePager">' );
			foreach ( $res as $row ) {
				$voter = $this->context->getVoter( $row->voter );
				$wgOut->addHTML(
					'<tr>' .
					'<td>' . htmlspecialchars( $wgLang->timeanddate( $row->cm_timestamp ) ) . '</td>' . 
					'<td>' . 
					Xml::element( 
						'a', 
						array( 'href' => $voter->getUrl() ), 
						$voter->getName() . '@' . $voter->getDomain()
					) .
					'</td></tr>'
				);
			}
			$wgOut->addHTML( '</table>' );
		}		

		# Show strike log
		$wgOut->addHTML( '<h2>' . wfMsgHTML( 'securepoll-strike-log' ) . "</h2>\n" );
		$pager = new SecurePoll_StrikePager( $this, $this->voteId );
		$wgOut->addHTML(
			$pager->getBody() .
			$pager->getNavigationBar()
		);
	}