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