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