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