Check correct voting in ballot.
[usenet/usevote.git] / UVmenu.pm
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,                             #
21 #             Voting (references to strings)                                 #
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 {
29   my ($votes, $header, $body, $addr, $name, $ballot_id, $voting, $set, $errors) = @_;
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});
83       print "(7) ", UVmessage::get("MENU_VOTING"), " [", $$voting, "]\n";
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
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 =~ /^i$/i) {
291       my $ignore = UVmessage::get("MENU_IGNORE_STRING");
292       # Set columns for Text::Wrap
293       $columns = $config{rightmargin};
294       print wrap('', '', UVmessage::get("MENU_IGNORE_WARNING",
295                                         (MENU_IGNORE_STRING => $ignore)
296                                        ));
297       if (<STDIN> eq "$ignore\n") {
298         print "\n";
299         return "i";
300       }
301
302     } elsif ($input =~ /^w$/i) {
303
304       if (keys %errors) {
305         if ((keys %errors)==1 && $errors{UnrecognizedVote}) {
306           # unrecognized vote lines aren't errors if votetaker
307           # did not change them
308           @$errors = ();
309         } else {
310           # Set columns for Text::Wrap
311           $columns = $config{rightmargin};
312           @$errors = keys %errors;
313           my $warning = ' ' . UVmessage::get("MENU_ERROR_WARNING") . ' ';
314           my $length = length($warning);
315           print "\n", '*' x (($config{rightmargin}-$length)/2), $warning,
316                 '*' x (($config{rightmargin}-$length)/2), "\n\n",
317                 wrap('', '', UVmessage::get("MENU_ERROR_TEXT")), "\n\n",
318                 '*' x $config{rightmargin}, "\n\n",
319                 UVmessage::get("MENU_ERROR_GETKEY");
320           my $input = <STDIN>;
321           next if ($input !~ /^y$/i);
322           print "\n";
323         }
324       } else {
325         @$errors = ();
326       }
327  
328       system($config{clearcmd});
329       print "\n", UVmessage::get("MENU_PROCESSING"), "\n";
330       return "w";
331     }
332   }
333
334   sub check_ballotid {
335     my ($errors, $voter_addr, $ballot_id, $ids) = @_;
336
337     return 0 unless ($config{personal});
338
339     delete $errors->{NoBallotID};
340     delete $errors->{WrongBallotID};
341     delete $errors->{AddressNotRegistered};
342
343     if ($$ballot_id) {
344       if ($ids->{$$voter_addr}) {
345         if ($ids->{$$voter_addr} ne $$ballot_id) {
346           # ballot id incorrect
347           $errors->{WrongBallotID} = UVmessage::get("MENU_WRONGBALLOTID");
348         }
349       } else {
350         $errors->{AddressNotRegistered} = UVmessage::get("MENU_ADDRESSNOTREGISTERED");
351       } 
352     } else {
353       $errors->{NoBallotID} = UVmessage::get("MENU_NOBALLOTID");
354     }
355   }
356
357 }
358
359
360 ##############################################################################
361 # Menu for sorting out duplicate votings manually                            #
362 # Parameters: References to hashes with the paragraphs from the result file  #
363 #             and the default value                                          #
364 # Return value: selected menu item (1, 2 or 0)                               #
365 ##############################################################################
366
367 sub dup_choice {
368   my ($vote1, $vote2, $default) = @_;
369
370   print STDERR "\n", UVmessage::get("MENU_DUP_VOTE"), "\n\n";
371   print STDERR UVmessage::get("MENU_DUP_FIRST"), "\n";
372   print STDERR "A: $vote1->{A}\n";
373   print STDERR "N: $vote1->{N}\n";
374   print STDERR "D: $vote1->{D}\n";
375   print STDERR "K: $vote1->{K}\n";
376   print STDERR "S: $vote1->{S}\n\n";
377   print STDERR UVmessage::get("MENU_DUP_SECOND"), "\n";
378   print STDERR "A: $vote2->{A}\n";
379   print STDERR "N: $vote2->{N}\n";
380   print STDERR "D: $vote2->{D}\n";
381   print STDERR "K: $vote2->{K}\n";
382   print STDERR "S: $vote2->{S}\n\n";
383   print STDERR "1: ", UVmessage::get("MENU_DUP_DELFIRST"), "\n",
384                "2: ", UVmessage::get("MENU_DUP_DELSECOND"), "\n",
385                "0: ", UVmessage::get("MENU_DUP_DELNONE"), "\n\n";
386
387   my $input;
388
389   do {
390     print STDERR UVmessage::get("MENU_PROMPT"), "[$default] ";
391     $input = <STDIN>;
392     chomp $input;
393   } until ($input eq '' || ($input >= 0 && $input<3));
394
395   return $input || $default;
396 }
397
398 1;
This page took 0.021732 seconds and 4 git commands to generate.