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