Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 115
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
ListPage
0.00% covered (danger)
0.00%
0 / 115
0.00% covered (danger)
0.00%
0 / 4
182
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 1
72
 strike
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
12
 getTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\SecurePoll\Pages;
4
5use JobQueueGroup;
6use JobSpecification;
7use MediaWiki\Extension\SecurePoll\Entities\Election;
8use MediaWiki\Extension\SecurePoll\SpecialSecurePoll;
9use MediaWiki\Status\Status;
10use MediaWiki\Title\Title;
11
12/**
13 * SecurePoll subpage to show a list of votes for a given election.
14 * Provides an administrator interface for striking fraudulent votes.
15 */
16class ListPage extends ActionPage {
17    /** @var Election */
18    public $election;
19
20    /** @var JobQueueGroup */
21    private $jobQueueGroup;
22
23    /**
24     * @param SpecialSecurePoll $specialPage
25     * @param JobQueueGroup $jobQueueGroup
26     */
27    public function __construct(
28        SpecialSecurePoll $specialPage,
29        JobQueueGroup $jobQueueGroup
30    ) {
31        parent::__construct( $specialPage );
32        $this->jobQueueGroup = $jobQueueGroup;
33    }
34
35    /**
36     * Execute the subpage.
37     * @param array $params Array of subpage parameters.
38     */
39    public function execute( $params ) {
40        $out = $this->specialPage->getOutput();
41        $out->enableOOUI();
42
43        if ( !count( $params ) ) {
44            $out->addWikiMsg( 'securepoll-too-few-params' );
45
46            return;
47        }
48
49        $electionId = intval( $params[0] );
50        $this->election = $this->context->getElection( $electionId );
51        if ( !$this->election ) {
52            $out->addWikiMsg( 'securepoll-invalid-election', $electionId );
53
54            return;
55        }
56        $this->initLanguage( $this->specialPage->getUser(), $this->election );
57
58        $out->setPageTitleMsg( $this->msg( 'securepoll-list-title', $this->election->getMessage( 'title' ) ) );
59
60        $isAdmin = $this->election->isAdmin( $this->specialPage->getUser() );
61
62        if ( $this->election->getProperty( 'voter-privacy' ) && !$isAdmin ) {
63            $out->addWikiMsg( 'securepoll-list-private' );
64
65            return;
66        }
67
68        $dbr = $this->election->context->getDB( DB_REPLICA );
69
70        $res = $dbr->newSelectQueryBuilder()
71            ->select( 'vote_voter' )
72            ->distinct()
73            ->from( 'securepoll_votes' )
74            ->where( [
75                'vote_election' => $this->election->getId()
76            ] )
77            ->caller( __METHOD__ )
78            ->fetchResultSet();
79        $distinct_voters = $res->numRows();
80
81        $res = $dbr->newSelectQueryBuilder()
82            ->select( 'vote_id' )
83            ->from( 'securepoll_votes' )
84            ->where( [
85                'vote_election' => $this->election->getId()
86            ] )
87            ->caller( __METHOD__ )
88            ->fetchResultSet();
89        $all_votes = $res->numRows();
90
91        $res = $dbr->newSelectQueryBuilder()
92            ->select( 'vote_id' )
93            ->from( 'securepoll_votes' )
94            ->where( [
95                'vote_election' => $this->election->getId(),
96                'vote_current' => 0
97            ] )
98            ->caller( __METHOD__ )
99            ->fetchResultSet();
100        $not_current_votes = $res->numRows();
101
102        $res = $dbr->newSelectQueryBuilder()
103            ->select( 'vote_id' )
104            ->from( 'securepoll_votes' )
105            ->where( [
106                'vote_election' => $this->election->getId(),
107                'vote_struck' => 1
108            ] )
109            ->caller( __METHOD__ )
110            ->fetchResultSet();
111        $struck_votes = $res->numRows();
112
113        $out->addHTML(
114            '<div id="mw-poll-stats">' . $this->msg( 'securepoll-voter-stats' )->numParams(
115                $distinct_voters
116            )->parseAsBlock() . $this->msg( 'securepoll-vote-stats' )->numParams(
117                    $all_votes,
118                    $not_current_votes,
119                    $struck_votes
120                )->parseAsBlock() . '</div>'
121        );
122
123        $pager = new ListPager( $this );
124
125        // If an admin is viewing the votes, log it
126        $securePollUseLogging = $this->specialPage->getConfig()->get( 'SecurePollUseLogging' );
127        if ( $isAdmin && $securePollUseLogging ) {
128            $fields = [
129                'spl_election_id' => $electionId,
130                'spl_user' => $this->specialPage->getUser()->getId(),
131                'spl_type' => self::LOG_TYPE_VIEWVOTES,
132
133            ];
134            $this->jobQueueGroup->push(
135                new JobSpecification(
136                    'securePollLogAdminAction',
137                    [ 'fields' => $fields ],
138                    [],
139                    $this->getTitle()
140                )
141            );
142        }
143
144        $out->addHTML( $pager->getLimitForm() . $pager->getNavigationBar() );
145        $out->addParserOutputContent( $pager->getBodyOutput() );
146        $out->addHTML( $pager->getNavigationBar() );
147        if ( $isAdmin ) {
148            $out->addJsConfigVars( 'SecurePollSubPage', 'list' );
149            $out->addModules( 'ext.securepoll.htmlform' );
150        }
151    }
152
153    /**
154     * The strike/unstrike backend.
155     * @param string $action strike or unstrike
156     * @param int $voteId The vote ID
157     * @param string $reason The reason
158     * @return Status
159     */
160    public function strike( $action, $voteId, $reason ) {
161        $dbw = $this->context->getDB();
162        // this still gives the securepoll-need-admin error when an admin tries to
163        // delete a nonexistent vote.
164        if ( !$this->election->isAdmin( $this->specialPage->getUser() ) ) {
165            return Status::newFatal( 'securepoll-need-admin' );
166        }
167        if ( $action != 'strike' ) {
168            $action = 'unstrike';
169        }
170
171        $dbw->startAtomic( __METHOD__ );
172        // Add it to the strike log
173        $dbw->newInsertQueryBuilder()
174            ->insertInto( 'securepoll_strike' )
175            ->row( [
176                'st_vote' => $voteId,
177                'st_timestamp' => wfTimestampNow(),
178                'st_action' => $action,
179                'st_reason' => $reason,
180                'st_user' => $this->specialPage->getUser()->getId()
181            ] )
182            ->caller( __METHOD__ )
183            ->execute();
184        // Update the status cache
185        $dbw->newUpdateQueryBuilder()
186            ->update( 'securepoll_votes' )
187            ->set( [ 'vote_struck' => intval( $action == 'strike' ) ] )
188            ->where( [ 'vote_id' => $voteId ] )
189            ->caller( __METHOD__ )
190            ->execute();
191        $dbw->endAtomic( __METHOD__ );
192
193        return Status::newGood();
194    }
195
196    /**
197     * @return Title object
198     */
199    public function getTitle() {
200        return $this->specialPage->getPageTitle( 'list/' . $this->election->getId() );
201    }
202}