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) # | |
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 | ||
28 | sub 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 | ||
396 | sub 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 | ||
427 | 1; |