Add option to compare ballot to sample ballot.
[usenet/usevote.git] / UVmenu.pm
CommitLineData
ac7e2c54
TH
1# UVmenu: menu for interaction with the votetaker
2# Used by uvvote.pl, uvcfv.pl, uvcount.pl
3
4package UVmenu;
5
6use strict;
7use UVconfig;
8use UVmessage;
9use UVrules;
10use vars qw($VERSION);
11
12use Text::Wrap qw(wrap $columns);
13
14# Module version
15$VERSION = "0.4";
16
17##############################################################################
18# Menu for interaction with the votetaker #
19# Parameters: votes list and header (references to arrays) #
bb61da37
TH
20# Body, Mailadress, Name, Ballot ID, #
21# Voting (references to strings) #
ac7e2c54
TH
22# List of newly set fields (reference to array) #
23# List of errors to correct (Array-Ref) #
24# Return Values: 'w': proceed #
25# 'i': ignore (don't save vote) #
26##############################################################################
27
28sub menu {
bb61da37 29 my ($votes, $header, $body, $addr, $name, $ballot_id, $voting, $set, $errors) = @_;
ac7e2c54
TH
30 my $input = "";
31 my $voter_addr = $$addr || '';
32 my $voter_name = $$name || '';
33 my @newvotes = @$votes;
34 my $mailonly = 0;
35 my %errors;
36 $$ballot_id ||= '';
37
38 foreach my $error (@$errors) {
39
40 # unrecognized vote: extract group number und display warning
41 if ($error =~ /^UnrecognizedVote #(\d+)#(.+)$/) {
42 $errors{UnrecognizedVote} ||= UVmessage::get("MENU_UNRECOGNIZEDVOTE");
43 $errors{UnrecognizedVote} .= "\n " . UVmessage::get("MENU_UNRECOGNIZED_LIST")
44 . " #$1: $2";
45
46 # violated rule: extract rule number and display warning
47 } elsif ($error =~ /^ViolatedRule #(\d+)$/) {
48 $errors{ViolatedRule} ||= UVmessage::get("MENU_VIOLATEDRULE", (RULE => "#$1"));
49
50 } else {
51 # special handling if called from uvballot.pl
52 $mailonly = 1 if ($error =~ s/Ballot$//);
53
54 # get error message for this error from messages.cfg
55 $errors{$error} = UVmessage::get("MENU_" . uc($error));
56 }
57 }
58
59 # This loop is only left by 'return'
60 while (1) {
61
62 system($config{clearcmd});
63 print UVmessage::get("MENU_PROBLEMS") . "\n";
64
65 foreach my $error (keys %errors) {
66 print "* $errors{$error}\n";
67 }
68
69 my $menucaption = UVmessage::get("MENU_CAPTION");
70 print "\n\n$menucaption\n";
71 print "=" x length($menucaption), "\n\n";
e91a2b7f
TH
72
73 # don't print this option if called from uvcfv.pl
74 unless ($mailonly) {
75 print "(0) ", UVmessage::get("MENU_DIFF_BALLOT"), "\n";
76 }
77
ac7e2c54
TH
78 print "(1) ", UVmessage::get("MENU_SHOW_MAIL"), "\n\n",
79 UVmessage::get("MENU_CHANGE_PROPERTIES"), "\n",
80 "(2) ", UVmessage::get("MENU_ADDRESS"), " [$voter_addr]\n";
81
82 # don't print these options if called from uvcfv.pl
83 unless ($mailonly) {
84 print "(3) ", UVmessage::get("MENU_NAME"), " [$voter_name]\n";
85 print "(4) ", UVmessage::get("MENU_VOTES"), " [", @$votes, "]\n";
86 print "(5) ", UVmessage::get("MENU_BALLOT_ID"), " [$$ballot_id]\n"
87 if ($config{personal});
88 print "(6) ", UVmessage::get("MENU_BDSG"), "\n" if ($config{bdsg});
bb61da37 89 print "(7) ", UVmessage::get("MENU_VOTING"), " [", $$voting, "]\n";
ac7e2c54
TH
90 }
91
92 print "\n",
93 "(i) ", UVmessage::get("MENU_IGNORE"), "\n",
94 "(w) ", UVmessage::get("MENU_PROCEED"), "\n\n",
95 UVmessage::get("MENU_PROMPT");
96
97 do { $input = <STDIN>; } until ($input);
98 chomp $input;
99 print "\n";
100
101 # only accept 1, 2, i and w if called from uvcfv.pl
102 next if ($mailonly && $input !~ /^[12iw]$/i);
103
e91a2b7f
TH
104 if ($input eq '0') {
105 # ignore SIGPIPE (Bug in more and less)
106 $SIG{PIPE} = 'IGNORE';
107 open (DIFF, "|$config{diff} - $config{sampleballotfile} | $config{pager}");
108 print DIFF $$body, "\n";
109 close (DIFF);
110
111 } elsif ($input eq '1') {
ac7e2c54
TH
112 system($config{clearcmd});
113 # ignore SIGPIPE (Bug in more and less)
114 $SIG{PIPE} = 'IGNORE';
115 open (MORE, "|$config{pager}");
116 print MORE join("\n", @$header), "\n\n", $$body, "\n";
117 close (MORE);
118
119 print "\n", UVmessage::get("MENU_GETKEY");
120 $input = <STDIN>;
121
122 } elsif ($input eq '2') {
123 my $sel;
124 do {
125 print "[a] ", UVmessage::get("MENU_ADDRESS_OK"), "\n",
126 "[b] ", UVmessage::get("MENU_ADDRESS_CHANGE"), "\n",
127 "[c] ", UVmessage::get("MENU_ADDRESS_INVALID"), "\n\n",
128 UVmessage::get("MENU_PROMPT");
129 $sel = <STDIN>;
130 } until ($sel =~ /^[abc]$/i);
131 if ($sel =~ /^a$/i) {
132 delete $errors{SuspiciousAccount};
133 delete $errors{InvalidAddress};
134 next;
135 } elsif ($sel =~ /^c$/i) {
136 delete $errors{SuspiciousAccount};
137 $errors{InvalidAddress} = UVmessage::get("MENU_INVALIDADDRESS") . " " .
138 UVmessage::get("MENU_INVALIDADDRESS2");
139 next;
140 }
141
142 do {
143 print "\n", UVmessage::get("MENU_ADDRESS_PROMPT"), " ";
144 $voter_addr = <STDIN>;
145 chomp ($voter_addr);
146 } until ($voter_addr);
147 $$addr = $voter_addr;
148 push (@$set, 'Adresse');
149 delete $errors{SuspiciousAccount};
150 delete $errors{InvalidAddress};
151 check_ballotid(\%errors, \$voter_addr, $ballot_id, \%ids);
152
153 } elsif ($input eq '3') {
154 my $sel;
155 do {
156 print "[a] ", UVmessage::get("MENU_NAME_OK"), "\n",
157 "[b] ", UVmessage::get("MENU_NAME_CHANGE"), "\n",
158 "[c] ", UVmessage::get("MENU_NAME_INVALID"), "\n\n",
159 UVmessage::get("MENU_PROMPT");
160 $sel = <STDIN>;
161 } until ($sel =~ /^[abc]$/i);
162 if ($sel =~ /^a$/i) {
163 delete $errors{InvalidName};
164 next;
165 } elsif ($sel =~ /^c$/i) {
166 $errors{InvalidName} = UVmessage::get("MENU_INVALIDNAME");
167 next;
168 }
169 print UVmessage::get("MENU_NAME"), ": ";
170 $voter_name = <STDIN>;
171 chomp ($voter_name);
172 $$name = $voter_name;
173 push (@$set, 'Name');
174 delete $errors{NoName};
175 delete $errors{InvalidName};
176
177 $errors{InvalidName} = UVmessage::get("MENU_INVALIDNAME")
178 unless ($voter_name =~ /$config{name_re}/);
179
180 } elsif ($input eq '4') {
181 # set votes
182
183 my $sel;
184 do {
185 print "[a] ", UVmessage::get("MENU_VOTES_OK"), "\n",
186 "[b] ", UVmessage::get("MENU_VOTES_RESET"), "\n",
187 "[c] ", UVmessage::get("MENU_VOTES_INVALID"), "\n",
188 "[d] ", UVmessage::get("MENU_VOTES_CANCELLED"), "\n\n",
189 UVmessage::get("MENU_PROMPT");
190 $sel = <STDIN>;
191 } until ($sel =~ /^[abcd]$/i);
192 if ($sel =~ /^[ad]$/i) {
193 delete $errors{NoVote};
194 delete $errors{UnrecognizedVote};
195 delete $errors{ViolatedRule};
196 delete $errors{DuplicateVote};
197 if ($sel =~ /^d$/i) {
198 # cancelled vote: replace all votes with an A
199 @$votes = split(//, 'A' x scalar @groups);
200 push @$set, 'Stimmen';
201 # some errors are irrelevant when cancelling a vote:
202 delete $errors{InvalidName};
203 delete $errors{NoName};
204 delete $errors{InvalidBDSG};
205 delete $errors{InvalidAddress};
206 delete $errors{SuspiciousAccount};
207 }
208 next;
209 } elsif ($sel =~ /^c$/i) {
210 $errors{NoVote} = UVmessage::get("MENU_INVALIDVOTE");
211 next;
212 }
213
214 # Set columns for Text::Wrap
215 $columns = $config{rightmargin};
216 print "\n", wrap('', '', UVmessage::get("MENU_VOTES_REENTER_ASK")), "\n\n";
217 print UVmessage::get("MENU_VOTES_REENTER_LEGEND"), "\n";
218
219 for (my $n=0; $n<@groups; $n++) {
220 my $voteinput = "";
221 $votes->[$n] ||= 'E';
222
223 # repeat while invalid character entered
224 while (!($voteinput =~ /^[JNE]$/)) {
225 my $invalid = $#groups ? 0 : 1;
226 print UVmessage::get("MENU_VOTES_REENTER", (GROUP => $groups[$n]));
227 $voteinput = <STDIN>;
228 chomp $voteinput;
229 $voteinput ||= $votes->[$n];
230 $voteinput =~ tr/jne/JNE/;
231 }
232
233 # valid input, save new votes
234 $newvotes[$n] = $voteinput;
235 }
236
237 print "\n\n";
238 my $oldvotes = UVmessage::get("MENU_VOTES_REENTER_OLD");
239 my $newvotes = UVmessage::get("MENU_VOTES_REENTER_NEW");
240 my $oldlen = length($oldvotes);
241 my $newlen = length($newvotes);
242 my $maxlen = 1 + (($newlen>$oldlen) ? $newlen : $oldlen);
243 print $oldvotes, ' ' x ($maxlen - length($oldvotes)), @$votes, "\n",
244 $newvotes, ' ' x ($maxlen - length($newvotes)), @newvotes, "\n\n";
245
246 do {
247 print "[a] ", UVmessage::get("MENU_VOTES_REENTER_ACK"), " ",
248 "[b] ", UVmessage::get("MENU_VOTES_REENTER_NACK"), "\n\n",
249 UVmessage::get("MENU_PROMPT");
250 $sel = <STDIN>;
251 } until ($sel =~ /^[ab]$/i);
252
253 next if ($sel =~ /^b$/i);
254 @$votes = @newvotes;
255 push @$set, 'Stimmen';
256 delete $errors{UnrecognizedVote};
257 delete $errors{DuplicateVote};
258 delete $errors{NoVote};
259 delete $errors{ViolatedRule};
260
261 if (my $rule = UVrules::rule_check($votes)) {
262 $errors{ViolatedRule} = UVmessage::get("MENU_VIOLATEDRULE", (RULE => "#$rule"));
263 }
264
265 } elsif ($input eq '5' && $config{personal}) {
266 print "\n", UVmessage::get("MENU_BALLOT_ID"), ": ";
267 $$ballot_id = <STDIN>;
268 chomp ($$ballot_id);
269 push (@$set, 'Kennung');
270 check_ballotid(\%errors, \$voter_addr, $ballot_id, \%ids);
271
272 } elsif ($input eq '6' && $config{bdsg}) {
273 my $sel;
274 do {
275 print "[a] ", UVmessage::get("MENU_BDSG_ACCEPTED"), "\n",
276 "[b] ", UVmessage::get("MENU_BDSG_DECLINED"), "\n\n",
277 UVmessage::get("MENU_PROMPT");
278 $sel = <STDIN>;
279 } until ($sel =~ /^[ab]$/i);
280
281 if ($sel =~ /^a$/i) {
282 delete $errors{InvalidBDSG};
283 } else {
284 $errors{InvalidBDSG} = UVmessage::get("MENU_INVALIDBDSG");
285 }
286
bb61da37
TH
287 } elsif ($input eq '7') {
288 my $sel;
289 do {
290 print "[a] ", UVmessage::get("MENU_VOTING_CORRECT"), "\n",
291 "[b] ", UVmessage::get("MENU_VOTING_WRONG"), "\n\n",
292 UVmessage::get("MENU_PROMPT");
293 $sel = <STDIN>;
294 } until ($sel =~ /^[ab]$/i);
295
296 if ($sel =~ /^a$/i) {
297 delete $errors{NoVoting};
298 delete $errors{WrongVoting};
299 } else {
300 $errors{WrongVoting} = UVmessage::get("MENU_WRONGVOTING");
301 }
302
303 } elsif ($input eq '7') {
304 my $sel;
305 do {
306 print "[a] ", UVmessage::get("MENU_VOTING_CORRECT"), "\n",
307 "[b] ", UVmessage::get("MENU_VOTING_WRONG"), "\n\n",
308 UVmessage::get("MENU_PROMPT");
309 $sel = <STDIN>;
310 } until ($sel =~ /^[ab]$/i);
311
312 if ($sel =~ /^a$/i) {
313 delete $errors{NoVoting};
314 delete $errors{WrongVoting};
315 } else {
316 $errors{WrongVoting} = UVmessage::get("MENU_WRONGVOTING");
317 }
318
ac7e2c54
TH
319 } elsif ($input =~ /^i$/i) {
320 my $ignore = UVmessage::get("MENU_IGNORE_STRING");
321 # Set columns for Text::Wrap
322 $columns = $config{rightmargin};
323 print wrap('', '', UVmessage::get("MENU_IGNORE_WARNING",
324 (MENU_IGNORE_STRING => $ignore)
325 ));
326 if (<STDIN> eq "$ignore\n") {
327 print "\n";
328 return "i";
329 }
330
331 } elsif ($input =~ /^w$/i) {
332
333 if (keys %errors) {
334 if ((keys %errors)==1 && $errors{UnrecognizedVote}) {
335 # unrecognized vote lines aren't errors if votetaker
336 # did not change them
337 @$errors = ();
338 } else {
339 # Set columns for Text::Wrap
340 $columns = $config{rightmargin};
341 @$errors = keys %errors;
342 my $warning = ' ' . UVmessage::get("MENU_ERROR_WARNING") . ' ';
343 my $length = length($warning);
344 print "\n", '*' x (($config{rightmargin}-$length)/2), $warning,
345 '*' x (($config{rightmargin}-$length)/2), "\n\n",
346 wrap('', '', UVmessage::get("MENU_ERROR_TEXT")), "\n\n",
347 '*' x $config{rightmargin}, "\n\n",
348 UVmessage::get("MENU_ERROR_GETKEY");
349 my $input = <STDIN>;
350 next if ($input !~ /^y$/i);
351 print "\n";
352 }
353 } else {
354 @$errors = ();
355 }
356
357 system($config{clearcmd});
358 print "\n", UVmessage::get("MENU_PROCESSING"), "\n";
359 return "w";
360 }
361 }
362
363 sub check_ballotid {
364 my ($errors, $voter_addr, $ballot_id, $ids) = @_;
365
366 return 0 unless ($config{personal});
367
368 delete $errors->{NoBallotID};
369 delete $errors->{WrongBallotID};
370 delete $errors->{AddressNotRegistered};
371
372 if ($$ballot_id) {
373 if ($ids->{$$voter_addr}) {
374 if ($ids->{$$voter_addr} ne $$ballot_id) {
375 # ballot id incorrect
376 $errors->{WrongBallotID} = UVmessage::get("MENU_WRONGBALLOTID");
377 }
378 } else {
379 $errors->{AddressNotRegistered} = UVmessage::get("MENU_ADDRESSNOTREGISTERED");
380 }
381 } else {
382 $errors->{NoBallotID} = UVmessage::get("MENU_NOBALLOTID");
383 }
384 }
385
386}
387
388
389##############################################################################
390# Menu for sorting out duplicate votings manually #
391# Parameters: References to hashes with the paragraphs from the result file #
392# and the default value #
393# Return value: selected menu item (1, 2 or 0) #
394##############################################################################
395
396sub dup_choice {
397 my ($vote1, $vote2, $default) = @_;
398
399 print STDERR "\n", UVmessage::get("MENU_DUP_VOTE"), "\n\n";
400 print STDERR UVmessage::get("MENU_DUP_FIRST"), "\n";
401 print STDERR "A: $vote1->{A}\n";
402 print STDERR "N: $vote1->{N}\n";
403 print STDERR "D: $vote1->{D}\n";
404 print STDERR "K: $vote1->{K}\n";
405 print STDERR "S: $vote1->{S}\n\n";
406 print STDERR UVmessage::get("MENU_DUP_SECOND"), "\n";
407 print STDERR "A: $vote2->{A}\n";
408 print STDERR "N: $vote2->{N}\n";
409 print STDERR "D: $vote2->{D}\n";
410 print STDERR "K: $vote2->{K}\n";
411 print STDERR "S: $vote2->{S}\n\n";
412 print STDERR "1: ", UVmessage::get("MENU_DUP_DELFIRST"), "\n",
413 "2: ", UVmessage::get("MENU_DUP_DELSECOND"), "\n",
414 "0: ", UVmessage::get("MENU_DUP_DELNONE"), "\n\n";
415
416 my $input;
417
418 do {
419 print STDERR UVmessage::get("MENU_PROMPT"), "[$default] ";
420 $input = <STDIN>;
421 chomp $input;
422 } until ($input eq '' || ($input >= 0 && $input<3));
423
424 return $input || $default;
425}
426
4271;
This page took 0.029836 seconds and 4 git commands to generate.