Index: trunk/extensions/SecurePoll/SecurePoll.i18n.php |
— | — | @@ -100,8 +100,8 @@ |
101 | 101 | 'securepoll-strike-error' => 'Error performing strike/unstrike: $1', |
102 | 102 | 'securepoll-strike-token-mismatch' => 'Session data lost', |
103 | 103 | 'securepoll-details-link' => 'Details', |
104 | | - 'securepoll-votername-local' => '[[User:$1|$1]]', |
105 | | - 'securepoll-votername-remote' => '$1', |
| 104 | + 'securepoll-voter-name-local' => '[[User:$1|$1]]', |
| 105 | + 'securepoll-voter-name-remote' => '$1', |
106 | 106 | |
107 | 107 | # Details page |
108 | 108 | # Mostly for admins |
— | — | @@ -162,6 +162,7 @@ |
163 | 163 | 'securepoll-pairwise-victories' => 'Pairwise victory matrix', |
164 | 164 | 'securepoll-strength-matrix' => 'Path strength matrix', |
165 | 165 | 'securepoll-ranks' => 'Final ranking', |
| 166 | + 'securepoll-average-score' => 'Average score', |
166 | 167 | ); |
167 | 168 | |
168 | 169 | /** Message documentation (Message documentation) |
Index: trunk/extensions/SecurePoll/SecurePoll.php |
— | — | @@ -89,6 +89,7 @@ |
90 | 90 | |
91 | 91 | # talliers |
92 | 92 | 'SecurePoll_ElectionTallier' => "$dir/includes/talliers/ElectionTallier.php", |
| 93 | + 'SecurePoll_HistogramRangeTallier' => "$dir/includes/talliers/HistogramRangeTallier.php", |
93 | 94 | 'SecurePoll_PairwiseTallier' => "$dir/includes/talliers/PairwiseTallier.php", |
94 | 95 | 'SecurePoll_PluralityTallier' => "$dir/includes/talliers/PluralityTallier.php", |
95 | 96 | 'SecurePoll_SchulzeTallier' => "$dir/includes/talliers/SchulzeTallier.php", |
Index: trunk/extensions/SecurePoll/includes/pages/EntryPage.php |
— | — | @@ -30,6 +30,28 @@ |
31 | 31 | * Pager for an election list. See TablePager documentation. |
32 | 32 | */ |
33 | 33 | class SecurePoll_ElectionPager extends TablePager { |
| 34 | + var $subpages = array( |
| 35 | + 'vote' => array( |
| 36 | + 'public' => true, |
| 37 | + 'visible-after-close' => false, |
| 38 | + ), |
| 39 | + 'translate' => array( |
| 40 | + 'public' => true, |
| 41 | + 'visible-after-close' => true, |
| 42 | + ), |
| 43 | + 'list' => array( |
| 44 | + 'public' => true, |
| 45 | + 'visible-after-close' => true, |
| 46 | + ), |
| 47 | + 'dump' => array( |
| 48 | + 'public' => false, |
| 49 | + 'visible-after-close' => true, |
| 50 | + ), |
| 51 | + 'tally' => array( |
| 52 | + 'public' => false, |
| 53 | + 'visible-after-close' => true, |
| 54 | + ), |
| 55 | + ); |
34 | 56 | var $fields = array( |
35 | 57 | 'el_title', |
36 | 58 | 'el_start_date', |
— | — | @@ -64,7 +86,7 @@ |
65 | 87 | * @return String |
66 | 88 | * @see TablePager::getRowClass() |
67 | 89 | */ |
68 | | - function getRowClass( $row ){ |
| 90 | + function getRowClass( $row ) { |
69 | 91 | return $row->el_end_date > wfTimestampNow( TS_DB ) |
70 | 92 | ? 'securepoll-election-open' |
71 | 93 | : 'securepoll-election-closed'; |
— | — | @@ -83,7 +105,7 @@ |
84 | 106 | } |
85 | 107 | } |
86 | 108 | |
87 | | - function formatRow( $row ){ |
| 109 | + function formatRow( $row ) { |
88 | 110 | global $wgUser; |
89 | 111 | $id = $row->el_entity; |
90 | 112 | $this->election = $this->entryPage->context->getElection( $id ); |
— | — | @@ -98,30 +120,23 @@ |
99 | 121 | function getLinks() { |
100 | 122 | global $wgUser; |
101 | 123 | $id = $this->mCurrentRow->el_entity; |
102 | | - |
103 | | - $links = array( |
104 | | - # visible to non-admins |
105 | | - # visible after election is closed |
106 | | - 'vote' => array( true, false ), |
107 | | - 'translate' => array( false, false ), |
108 | | - 'list' => array( true, true ), |
109 | | - 'dump' => array( false, true ), |
110 | | - 'tally' => array( false, true ), |
111 | | - ); |
112 | 124 | |
113 | 125 | $s = ''; |
114 | 126 | $sep = wfMsg( 'pipe-separator' ); |
115 | 127 | $skin = $wgUser->getSkin(); |
116 | | - foreach ( $links as $subpage => $criteria ) { |
117 | | - if( ( $this->isAdmin || $criteria[0] ) |
118 | | - && ( !$this->election->isFinished() || $criteria[1] ) |
119 | | - ){ |
| 128 | + foreach ( $this->subpages as $subpage => $props ) { |
| 129 | + $linkText = wfMsgExt( "securepoll-subpage-$subpage", 'parseinline' ); |
| 130 | + if ( $s !== '' ) { |
| 131 | + $s .= $sep; |
| 132 | + } |
| 133 | + if( ( $this->isAdmin || $props['public'] ) |
| 134 | + && ( !$this->election->isFinished() || $props['visible-after-close'] ) ) |
| 135 | + { |
120 | 136 | $title = $this->entryPage->parent->getTitle( "$subpage/$id" ); |
121 | | - $linkText = wfMsgExt( "securepoll-subpage-$subpage", 'parseinline' ); |
122 | | - if ( $s !== '' ) { |
123 | | - $s .= $sep; |
124 | | - } |
125 | 137 | $s .= $skin->makeKnownLinkObj( $title, $linkText ); |
| 138 | + } else { |
| 139 | + $s .= "<span class=\"securepoll-link-disabled\">" . |
| 140 | + $linkText . "</span>"; |
126 | 141 | } |
127 | 142 | } |
128 | 143 | return $s; |
Index: trunk/extensions/SecurePoll/includes/pages/ListPage.php |
— | — | @@ -269,8 +269,8 @@ |
270 | 270 | ) ); |
271 | 271 | case 'vote_voter_name': |
272 | 272 | $msg = $voter->isRemote() |
273 | | - ? 'securepoll-votername-remote' |
274 | | - : 'securepoll-votername-local'; |
| 273 | + ? 'securepoll-voter-name-remote' |
| 274 | + : 'securepoll-voter-name-local'; |
275 | 275 | return wfMsgExt( |
276 | 276 | $msg, |
277 | 277 | 'parseinline', |
Index: trunk/extensions/SecurePoll/includes/talliers/Tallier.php |
— | — | @@ -6,7 +6,7 @@ |
7 | 7 | * questions. |
8 | 8 | */ |
9 | 9 | abstract class SecurePoll_Tallier { |
10 | | - var $context, $question, $optionsById; |
| 10 | + var $context, $question, $electionTallier, $election, $optionsById; |
11 | 11 | |
12 | 12 | abstract function addVote( $scores ); |
13 | 13 | abstract function getHtmlResult(); |
— | — | @@ -14,20 +14,24 @@ |
15 | 15 | |
16 | 16 | abstract function finishTally(); |
17 | 17 | |
18 | | - static function factory( $context, $type, $question ) { |
| 18 | + static function factory( $context, $type, $electionTallier, $question ) { |
19 | 19 | switch ( $type ) { |
20 | 20 | case 'plurality': |
21 | | - return new SecurePoll_PluralityTallier( $context, $question ); |
| 21 | + return new SecurePoll_PluralityTallier( $context, $electionTallier, $question ); |
22 | 22 | case 'schulze': |
23 | | - return new SecurePoll_SchulzeTallier( $context, $question ); |
| 23 | + return new SecurePoll_SchulzeTallier( $context, $electionTallier, $question ); |
| 24 | + case 'histogram-range': |
| 25 | + return new SecurePoll_HistogramRangeTallier( $context, $electionTallier, $question ); |
24 | 26 | default: |
25 | 27 | throw new MWException( "Invalid tallier type: $type" ); |
26 | 28 | } |
27 | 29 | } |
28 | 30 | |
29 | | - function __construct( $context, $question ) { |
| 31 | + function __construct( $context, $electionTallier, $question ) { |
30 | 32 | $this->context = $context; |
31 | 33 | $this->question = $question; |
| 34 | + $this->electionTallier = $electionTallier; |
| 35 | + $this->election = $electionTallier->election; |
32 | 36 | foreach ( $this->question->getOptions() as $option ) { |
33 | 37 | $this->optionsById[$option->getId()] = $option; |
34 | 38 | } |
Index: trunk/extensions/SecurePoll/includes/talliers/ElectionTallier.php |
— | — | @@ -32,7 +32,7 @@ |
33 | 33 | $this->talliers = array(); |
34 | 34 | $tallyType = $this->election->getTallyType(); |
35 | 35 | foreach ( $questions as $question ) { |
36 | | - $tallier = $this->context->newTallier( $tallyType, $question ); |
| 36 | + $tallier = $this->context->newTallier( $tallyType, $this, $question ); |
37 | 37 | if ( !$tallier ) { |
38 | 38 | throw new MWException( 'Invalid tally type' ); |
39 | 39 | } |
Index: trunk/extensions/SecurePoll/includes/talliers/PairwiseTallier.php |
— | — | @@ -10,8 +10,8 @@ |
11 | 11 | var $abbrevs; |
12 | 12 | var $rowLabels = array(); |
13 | 13 | |
14 | | - function __construct( $context, $question ) { |
15 | | - parent::__construct( $context, $question ); |
| 14 | + function __construct( $context, $electionTallier, $question ) { |
| 15 | + parent::__construct( $context, $electionTallier, $question ); |
16 | 16 | $this->optionIds = array(); |
17 | 17 | foreach ( $question->getOptions() as $option ) { |
18 | 18 | $this->optionIds[] = $option->getId(); |
Index: trunk/extensions/SecurePoll/includes/talliers/PluralityTallier.php |
— | — | @@ -6,8 +6,8 @@ |
7 | 7 | class SecurePoll_PluralityTallier extends SecurePoll_Tallier { |
8 | 8 | var $tally = array(); |
9 | 9 | |
10 | | - function __construct( $context, $question ) { |
11 | | - parent::__construct( $context, $question ); |
| 10 | + function __construct( $context, $electionTallier, $question ) { |
| 11 | + parent::__construct( $context, $electionTallier, $question ); |
12 | 12 | foreach ( $question->getOptions() as $option ) { |
13 | 13 | $this->tally[$option->getId()] = 0; |
14 | 14 | } |
Index: trunk/extensions/SecurePoll/includes/talliers/HistogramRangeTallier.php |
— | — | @@ -0,0 +1,86 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class SecurePoll_HistogramRangeTallier extends SecurePoll_Tallier { |
| 5 | + var $histogram = array(); |
| 6 | + var $sums = array(); |
| 7 | + var $counts = array(); |
| 8 | + var $averages; |
| 9 | + var $minScore, $maxScore; |
| 10 | + |
| 11 | + function __construct( $context, $electionTallier, $question ) { |
| 12 | + parent::__construct( $context, $electionTallier, $question ); |
| 13 | + $this->minScore = intval( $question->getProperty( 'min-score' ) ); |
| 14 | + $this->maxScore = intval( $question->getProperty( 'max-score' ) ); |
| 15 | + if ( $this->minScore >= $this->maxScore ) { |
| 16 | + throw new MWException( __METHOD__.': min-score/max-score configured incorrectly' ); |
| 17 | + } |
| 18 | + |
| 19 | + foreach ( $question->getOptions() as $option ) { |
| 20 | + $this->histogram[$option->getId()] = |
| 21 | + array_fill( $this->minScore, $this->maxScore - $this->minScore + 1, 0 ); |
| 22 | + $this->sums[$option->getId()] = 0; |
| 23 | + $this->counts[$option->getId()] = 0; |
| 24 | + } |
| 25 | + } |
| 26 | + |
| 27 | + function addVote( $scores ) { |
| 28 | + foreach ( $scores as $oid => $score ) { |
| 29 | + $this->histogram[$oid][$score] ++; |
| 30 | + $this->sums[$oid] += $score; |
| 31 | + $this->counts[$oid] ++; |
| 32 | + } |
| 33 | + return true; |
| 34 | + } |
| 35 | + |
| 36 | + function finishTally() { |
| 37 | + $this->averages = array(); |
| 38 | + foreach ( $this->sums as $oid => $sum ) { |
| 39 | + if ( $this->counts[$oid] === 0 ) { |
| 40 | + $this->averages[$oid] = 'N/A'; |
| 41 | + break; |
| 42 | + } |
| 43 | + $this->averages[$oid] = $sum / $this->counts[$oid]; |
| 44 | + } |
| 45 | + arsort( $this->averages ); |
| 46 | + } |
| 47 | + |
| 48 | + function getHtmlResult() { |
| 49 | + $ballot = $this->election->getBallot(); |
| 50 | + if ( !is_callable( array( $ballot, 'getColumnLabels' ) ) ) { |
| 51 | + throw new MWException( __METHOD__.': ballot type not supported by this tallier' ); |
| 52 | + } |
| 53 | + $optionLabels = array(); |
| 54 | + foreach ( $this->question->getOptions() as $option ) { |
| 55 | + $optionLabels[$option->getId()] = $option->getMessage( 'text' ); |
| 56 | + } |
| 57 | + |
| 58 | + $labels = $ballot->getColumnLabels( $this->question ); |
| 59 | + $s = "<table class=\"securepoll-table\">\n" . |
| 60 | + "<tr>\n" . |
| 61 | + "<th> </th>\n"; |
| 62 | + foreach ( $labels as $label ) { |
| 63 | + $s .= Xml::element( 'th', array(), $label ) . "\n"; |
| 64 | + } |
| 65 | + $s .= Xml::element( 'th', array(), wfMsg( 'securepoll-average-score' ) ); |
| 66 | + $s .= "</tr>\n"; |
| 67 | + |
| 68 | + foreach ( $this->averages as $oid => $average ) { |
| 69 | + $s .= "<tr>\n" . |
| 70 | + Xml::element( 'td', array( 'class' => 'securepoll-results-row-heading' ), |
| 71 | + $optionLabels[$oid] ) . |
| 72 | + "\n"; |
| 73 | + foreach ( $labels as $score => $label ) { |
| 74 | + $s .= Xml::element( 'td', array(), $this->histogram[$oid][$score] ) . "\n"; |
| 75 | + } |
| 76 | + $s .= Xml::element( 'td', array(), $average ) . "\n"; |
| 77 | + $s .= "</tr>\n"; |
| 78 | + } |
| 79 | + $s .= "</table>\n"; |
| 80 | + return $s; |
| 81 | + } |
| 82 | + |
| 83 | + function getTextResult() { |
| 84 | + throw new MWException( __METHOD__.': not yet implemented' ); |
| 85 | + } |
| 86 | +} |
| 87 | + |
Property changes on: trunk/extensions/SecurePoll/includes/talliers/HistogramRangeTallier.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 88 | + native |
Index: trunk/extensions/SecurePoll/includes/ballots/RadioRangeBallot.php |
— | — | @@ -86,6 +86,7 @@ |
87 | 87 | foreach ( $labels as $label ) { |
88 | 88 | $s .= Xml::element( 'th', array(), $label ) . "\n"; |
89 | 89 | } |
| 90 | + $s .= "</tr>\n"; |
90 | 91 | $defaultScore = $question->getProperty( 'default-score' ); |
91 | 92 | |
92 | 93 | foreach ( $options as $option ) { |
— | — | @@ -179,6 +180,7 @@ |
180 | 181 | list( $min, $max ) = $this->getMinMax( $questions[$qid] ); |
181 | 182 | if ( $score < $min || $score > $max ) { |
182 | 183 | wfDebug( __METHOD__.": score out of range\n" ); |
| 184 | + return false; |
183 | 185 | } |
184 | 186 | $scores[$qid][$oid] = $score; |
185 | 187 | } |
Index: trunk/extensions/SecurePoll/includes/main/Base.php |
— | — | @@ -36,6 +36,8 @@ |
37 | 37 | public function execute( $paramString ) { |
38 | 38 | global $wgOut, $wgUser, $wgRequest, $wgScriptPath; |
39 | 39 | |
| 40 | + wfLoadExtensionMessages( 'SecurePoll' ); |
| 41 | + |
40 | 42 | $this->setHeaders(); |
41 | 43 | $wgOut->addLink( array( |
42 | 44 | 'rel' => 'stylesheet', |
Index: trunk/extensions/SecurePoll/includes/main/Context.php |
— | — | @@ -251,8 +251,8 @@ |
252 | 252 | return SecurePoll_Crypt::factory( $this, $type, $election ); |
253 | 253 | } |
254 | 254 | |
255 | | - function newTallier( $type, $question ) { |
256 | | - return SecurePoll_Tallier::factory( $this, $type, $question ); |
| 255 | + function newTallier( $type, $electionTallier, $question ) { |
| 256 | + return SecurePoll_Tallier::factory( $this, $type, $electionTallier, $question ); |
257 | 257 | } |
258 | 258 | |
259 | 259 | function newBallot( $type, $election ) { |
Index: trunk/extensions/SecurePoll/cli/cli.inc |
— | — | @@ -14,3 +14,5 @@ |
15 | 15 | |
16 | 16 | require( $IP . '/maintenance/commandLine.inc' ); |
17 | 17 | |
| 18 | +wfLoadExtensionMessages( 'SecurePoll' ); |
| 19 | + |