source: trunk/lib/Vote/Model/Vote.pm @ 88

Last change on this file since 88 was 88, checked in by nanardon, 15 years ago
  • send mail to confirm ballot registration
File size: 20.3 KB
Line 
1package Vote::Model::Vote;
2
3use strict;
4use warnings;
5use base 'Catalyst::Model';
6use Vote;
7use DBI;
8use Mail::Mailer;
9
10=head1 NAME
11
12Vote::Model::Vote - Catalyst Model
13
14=head1 DESCRIPTION
15
16Catalyst Model.
17
18=cut
19
20sub new {
21    my ($class) = @_;
22    my $db = DBI->connect(
23        'dbi:Pg:' . Vote->config->{db},
24        undef, undef,
25        {
26            RaiseError => 0,
27            AutoCommit => 0,
28            PrintWarn => 0,
29            PrintError => 1,
30        }
31    ) or return;
32    $db->do(q{set DATESTYLE to 'DMY'});
33   
34    bless {
35        db => $db,
36    }, $class;
37}
38
39sub db { $_[0]->{db} }
40
41sub mail_header {
42    return(
43        'Content-Type' => 'text/plain; charset=UTF-8; format=flowed',
44        'Content-Transfer-Encoding' => '8bit',
45        'X-Epoll-version' => $Vote::VERSION,
46    );
47}
48
49sub random_string {
50    my $lenght = $_[-1] || 8;
51
52    return join('', map { ('a'..'z', 'A'..'Z', 0..9)[rand 62] } (1..$lenght));
53}
54
55sub gen_enc_passwd {
56    my ($self, $passwd) = @_;
57
58    $passwd ||= random_string(8);
59    return(crypt($passwd, '$1$' . random_string(8) . '$'));
60}
61
62sub list_comming_vote {
63    my ($self) = @_;
64
65    my $sth = $self->db->prepare_cached(
66        q{
67        select id from poll where
68        (start > now() and "end" > now()) or
69        "end" is null or start is null
70        }
71    );
72
73    $sth->execute;
74    my @id;
75    while(my $res = $sth->fetchrow_hashref) {
76        push(@id, $res->{id});
77    }
78
79    @id
80}
81
82
83sub list_running_vote {
84    my ($self) = @_;
85
86    my $sth = $self->db->prepare_cached(
87        q{
88        select id from poll where start < now() and "end" > now()
89        }
90    );
91
92    $sth->execute;
93    my @id;
94    while(my $res = $sth->fetchrow_hashref) {
95        push(@id, $res->{id});
96    }
97
98    @id
99}
100
101sub list_closed_vote {
102    my ($self) = @_;
103
104    my $sth = $self->db->prepare_cached(
105        q{
106        select id from poll where
107        start < now() and "end" < now()
108        }
109    );
110
111    $sth->execute;
112    my @id;
113    while(my $res = $sth->fetchrow_hashref) {
114        push(@id, $res->{id});
115    }
116
117    @id
118}
119
120sub vote_param {
121    my ($self, $voteid, %attr) = @_;
122
123    keys %attr or return;
124    my @online_f = qw(label start end owner password);
125
126    if (grep { exists($attr{$_}) } @online_f) {
127        my $sth = $self->db->prepare_cached(
128            q{update poll set } .
129            join(',', map { qq("$_" = ?) } grep { exists $attr{$_} } @online_f) .
130            q{ where id = ?}
131        );
132        $sth->execute((map { $attr{$_} } grep { exists $attr{$_} } @online_f), $voteid)
133            or do {
134            $self->db->rollback;
135            return;
136        };
137    }
138
139    # vote settings in settings table
140    foreach my $var (keys %attr) {
141        grep { $var eq $_ } @online_f and next;
142        $self->vote_set_settings($voteid, $var, $attr{$var});
143    }
144    1
145}
146
147sub vote_status {
148    my ($self, $id) = @_;
149   
150    my $sth = $self->db->prepare_cached(
151        q{
152        select (start > now() or start is null) as before,
153               "end" < now() as after
154        from poll
155        where id = ?
156        }
157    );
158    $sth->execute($id);
159    my $res = $sth->fetchrow_hashref;
160    $sth->finish;
161    $res or return;
162    if ($res->{before}) {
163        return 'BEFORE';
164    } elsif ($res->{after}) {
165        return 'AFTER';
166    } else {
167        return 'RUNNING';
168    }
169}
170
171sub vote_info {
172    my ($self, $id) = @_;
173
174    my $sth = $self->db->prepare_cached(
175        q{
176        select *,
177        to_char("start", 'DD/MM/YYYY') as dstart,
178        to_char("start", 'HH24:MI:SS') as hstart,
179        to_char("end", 'DD/MM/YYYY') as dend,
180        to_char("end", 'HH24:MI:SS') as hend
181        from poll where id = ?
182        }
183    );
184
185    $sth->execute($id);
186    my $res = $sth->fetchrow_hashref;
187    $sth->finish;
188    if ($res) {
189        my $get = $self->db->prepare_cached(
190            q{select var, val from settings where poll = ?}
191        );
192        $get->execute($id);
193        while (my $set = $get->fetchrow_hashref) {
194            $res->{$set->{var}} = $set->{val};
195        }
196    }
197    $res->{free_choice} ||= 0; # avoiding undef
198    $res
199}
200
201sub vote_set_settings {
202    my ($self, $poll, $var, $val) = @_;
203
204    my $upd = $self->db->prepare_cached(
205        q{update settings set val = ? where poll = ? and var = ?}
206    );
207
208    if ($upd->execute($val, $poll, $var) == 0) {
209        my $add = $self->db->prepare_cached(
210            q{insert into settings (poll, var, val) values (?,?,?)}
211        );
212
213        $add->execute($poll, $var, $val);
214    }
215}
216
217sub vote_signing {
218    my ($self, $id) = @_;
219
220    my $sth = $self->db->prepare_cached(
221        q{
222        select *, voting.key as vkey from voting left join signing
223        on signing.key = voting.key
224        where poll = ? order by voting.mail
225        }
226    );
227    $sth->execute($id);
228    my @people;
229    while (my $res = $sth->fetchrow_hashref) {
230        push(@people, $res);
231    }
232    @people
233}
234
235sub vote_voting {
236    my ($self, $voteid) = @_;
237
238    my $sth = $self->db->prepare_cached(
239        q{
240        select key from voting
241        where poll = ? order by voting.mail
242        }
243    );
244    $sth->execute($voteid);
245    my @people;
246    while (my $res = $sth->fetchrow_hashref) {
247        push(@people, $res->{key});
248    }
249    @people
250}
251
252sub voting_info {
253    my ($self, $id) = @_;
254
255    my $sth = $self->db->prepare_cached(
256        q{
257        select *, voting.key as vkey from voting left join signing
258        on signing.key = voting.key
259        where voting.key = ?
260        }
261    );
262    $sth->execute($id);
263
264    my $res = $sth->fetchrow_hashref;
265    $sth->finish;
266    $res
267}
268
269sub vote_choices {
270    my ($self, $id) = @_;
271
272    my $sth = $self->db->prepare_cached(
273        q{
274        select key from choice where poll = ?
275        order by label
276        }
277    );
278    $sth->execute($id);
279    my @ch;
280    while (my $res = $sth->fetchrow_hashref) {
281        push(@ch, $res->{key});
282    }
283    @ch
284}
285
286sub choice_info {
287    my ($self, $chid) = @_;
288    my $sth = $self->db->prepare_cached(
289        q{select * from choice where key = ?}
290    );
291    $sth->execute($chid);
292    my $res = $sth->fetchrow_hashref;
293    $sth->finish;
294    $res
295}
296
297sub vote_add_choice {
298    my ($self, $voteid, $label) = @_;
299
300    my $sth = $self->db->prepare_cached(
301        q{insert into choice (poll, label) values (?,?)}
302    );
303
304    $sth->execute($voteid, $label) or do {
305        $self->db->rollback;
306        return;
307    };
308
309    1
310}
311
312sub modify_choice {
313    my ($self, $chid, $label) = @_;
314
315    my $sth = $self->db->prepare_cached(
316        q{update choice set label = ? where key = ?}
317    );
318    $sth->execute($label, $chid);
319}
320
321sub delete_choice {
322    my ($self, $chid) = @_;
323
324    my $sth = $self->db->prepare_cached(
325        q{delete from choice where key = ?}
326    );
327
328    $sth->execute($chid);
329}
330
331sub voting_info_id {
332    my ($self, $mail, $voteid) = @_;
333
334    my $sth = $self->db->prepare_cached(
335        q{
336        select * from voting where mail = ? and poll = ?
337        }
338    );
339    $sth->execute($mail, $voteid);
340    my $res = $sth->fetchrow_hashref();
341    $sth->finish;
342    $res
343}
344
345sub _register_signing {
346    my ($self, $mail, $voteid, $referal) = @_;
347
348    my $vinfo = $self->voting_info_id($mail, $voteid) or return;
349
350    my $sth = $self->db->prepare_cached(
351        q{
352        insert into signing (key, referal) values (?,?)
353        }
354    );
355    $sth->execute($vinfo->{key}, $referal) or do {
356        $self->db->rollback;
357        return;
358    };
359
360    1;
361}
362
363sub gen_uid {
364    unpack("H*", join("", map { chr(rand(256)) } (0..15)))
365}
366
367sub _register_ballot {
368    my ($self, $voteid, $choice, $fchoice) = @_;
369
370    my $addb = $self->db->prepare_cached(
371        q{
372        insert into ballot (id, poll, invalid) values (?,?,?)
373        }
374    );
375    my $uid = gen_uid;
376    $addb->execute($uid, $voteid, scalar(@{$fchoice || []}) ? undef : 'f') or do {
377        self->db->rollback;
378        return;
379    };
380
381    my $addbc = $self->db->prepare_cached(
382        q{
383        insert into ballot_item (id, value, fromlist) values (?,?,?)
384        }
385    );
386    foreach (@{ $choice || []}) {
387        $addbc->execute($uid, $_, 't') or do {
388            $self->db->rollback;
389            return;
390        };
391    }
392    foreach (@{ $fchoice || []}) {
393        $_ or next;
394        $addbc->execute($uid, $_, 'f') or do {
395            $self->db->rollback;
396            return;
397        };
398    }
399
400    $uid;
401}
402
403sub register_ballot {
404    my ($self, $vid, $voteid, $choice, $fchoice, $referal) = @_;
405
406    # First we register voting has voted
407    $self->_register_signing($vid, $voteid, $referal) or return; # TODO error ?
408
409    # registring choices
410    my $uid = $self->_register_ballot($voteid, $choice, $fchoice) or return;
411
412    # everything went fine, saving!
413    $self->db->commit;
414
415   
416    $uid
417}
418
419sub mail_ballot_confirm {
420    my ($self, $vid, $voteid, $info) = @_;
421    my $voteinfo = $self->vote_info($voteid) or return;
422    $info->{ballotid} or return;
423    my $mailer = new Mail::Mailer 'smtp', Server => (Vote->config->{smtp} || 'localhost');
424    $mailer->open({
425        From => $vid, # TODO allow to configure this
426        To => $vid,
427        Subject => 'Confirmation de vote: ' . $voteinfo->{label},
428        mail_header(),
429    });
430    print $mailer <<EOF;
431
432Vous venez de participer au vote:
433
434--------
435$voteinfo->{label}
436--------
437
438Votre bulletin est idéntifié sous le numéro:
439$info->{ballotid}
440
441Les résultats seront disponibles à cet url:
442$info->{url}
443
444Cordialement.
445EOF
446    $mailer->close
447        or warn "couldn't send whole message: $!\n";
448
449}
450
451sub vote_voting_count {
452    my ($self, $id) = @_;
453
454    my $sth = $self->db->prepare_cached(
455        q{
456        select count(*) from voting
457        where poll = ?
458        }
459    );
460    $sth->execute($id);
461    my $res = $sth->fetchrow_hashref;
462    $sth->finish;
463    $res->{count}
464}
465
466sub signing_count { vote_signing_count(@_) }
467
468sub vote_signing_count {
469    my ($self, $voteid) = @_;
470
471    my $sth = $self->db->prepare_cached(
472        q{
473        select count(*) from signing join voting
474        on voting.key = signing.key where poll = ?
475        }
476    );
477
478    $sth->execute($voteid);
479    my $res = $sth->fetchrow_hashref;
480    $sth->finish;
481    $res->{count}
482}
483
484sub ballot_count { vote_ballot_count(@_) }
485
486sub vote_ballot_count {
487    my ($self, $voteid) = @_;
488
489    my $sth = $self->db->prepare_cached(
490        q{
491        select count(*) from ballot where poll = ?
492        }
493    );
494
495    $sth->execute($voteid);
496    my $res = $sth->fetchrow_hashref;
497    $sth->finish;
498    $res->{count}
499}
500
501sub ballot_count_nonull { vote_ballot_count_nonull(@_) }
502
503sub vote_ballot_count_nonull {
504    my ($self, $voteid) = @_;
505
506    my $sth = $self->db->prepare_cached(
507        q{
508        select count(*) from ballot where poll = ?
509        and id in (select id from ballot_item) and
510        (invalid = 'false' or invalid is null)
511        }
512    );
513
514    $sth->execute($voteid);
515    my $res = $sth->fetchrow_hashref;
516    $sth->finish;
517    $res->{count}
518}
519
520sub auth_voting {
521    my ($self, $poll, $mail, $password) = @_;
522    my $userinfo = $self->voting_info_id($mail, $poll) or return;
523
524    $userinfo->{passwd} or return;
525    if (crypt($password, $userinfo->{passwd} || '') eq $userinfo->{passwd}) {
526        return 1;
527    } else {
528        return 0;
529    }
530}
531
532sub auth_poll {
533    my ($self, $voteid, $passwd) = @_;
534
535    my $vinfo = $self->vote_info($voteid) or return;
536
537    $vinfo->{password} or return;
538    $passwd or return;
539    if (crypt($passwd, $vinfo->{password} || '') eq $vinfo->{password}) {
540        return 1;
541    } else {
542        return 0;
543    }
544}
545
546sub voting_has_sign {
547    my ($self, $poll, $user) = @_;
548
549    my $sth = $self->db->prepare_cached(
550        q{
551        select date from signing join voting
552        on voting.key = signing.key
553        where poll = ? and mail = ?
554        }
555    );
556
557    $sth->execute($poll, $user);
558    my $res = $sth->fetchrow_hashref;
559    $sth->finish;
560    return $res->{date}
561}
562
563# Requete de decompte des voix:
564
565sub vote_results_count {
566    my ($self, $voteid) = @_;
567
568    my $sth = $self->db->prepare(
569        q{
570        select count(ballot.id), value from ballot left join ballot_item
571        on ballot.id = ballot_item.id where ballot.poll = ? and invalid = 'false'
572        group by value
573        order by count
574        }
575    );
576    $sth->execute($voteid);
577    my @results;
578    while (my $res = $sth->fetchrow_hashref) {
579        push(@results, $res);
580    }
581    @results;
582}
583
584sub vote_results_nonull {
585    my ($self, $voteid) = @_;
586
587    my $sth = $self->db->prepare(
588        q{
589        select count(ballot.id), coalesce(corrected, value) as value
590        from ballot join ballot_item
591        on ballot.id = ballot_item.id where ballot.poll = ? and
592        (invalid = 'false' or invalid is null)
593        group by coalesce(corrected, value)
594        order by count desc
595        }
596    );
597    $sth->execute($voteid);
598    my @results;
599    while (my $res = $sth->fetchrow_hashref) {
600        push(@results, $res);
601    }
602    \@results;
603}
604
605sub list_vote_ballot {
606    my ($self, $voteid) = @_;
607
608    my $sth = $self->db->prepare_cached(
609        q{
610        select id from ballot where poll = ?
611        order by id
612        }
613    );
614    $sth->execute($voteid);
615    my @ids;
616    while (my $res = $sth->fetchrow_hashref) {
617        push(@ids, $res->{id});
618    }
619    @ids
620}
621
622sub list_vote_ballot_needvalid {
623    my ($self, $voteid) = @_;
624
625    my $sth = $self->db->prepare_cached(
626        q{
627        select id from ballot where poll = ?
628        and invalid is null order by id
629        }
630    );
631    $sth->execute($voteid);
632    my @ids;
633    while (my $res = $sth->fetchrow_hashref) {
634        push(@ids, $res->{id});
635    }
636    @ids
637}
638
639sub ballot_info {
640    my ($self, $ballotid) = @_;
641
642    my $sth = $self->db->prepare_cached(
643        q{ select * from ballot where id = ? }
644    );
645
646    $sth->execute($ballotid);
647    my $res = $sth->fetchrow_hashref;
648    $sth->finish;
649    $res
650}
651
652sub mark_ballot_invalid {
653    my ($self, $ballotid, $invalid) = @_;
654
655    my $sth = $self->db->prepare_cached(
656        q{update ballot set invalid = ? where id = ?}
657    );
658
659    $sth->execute($invalid ? 't' : 'f', $ballotid);
660}
661
662sub ballot_items {
663    my ($self, $ballotid) = @_;
664
665    my $sth = $self->db->prepare_cached(
666        q{select *, value as v from ballot_item where id = ?}
667    );
668    $sth->execute($ballotid);
669    my @ids;
670    while (my $res = $sth->fetchrow_hashref) {
671        push(@ids, $res);
672    }
673    \@ids
674}
675
676sub vote_ballot_untrusted_values {
677    my ($self, $voteid) = @_;
678
679    my $getval = $self->db->prepare_cached(
680        q{
681        select value from ballot join ballot_item
682        on ballot.id = ballot_item.id
683        where poll = ? and fromlist = false and corrected is null
684        group by value order by value
685        }
686    );
687    $getval->execute($voteid);
688    my @vals;
689    while (my $res = $getval->fetchrow_hashref) {
690        push(@vals, $res->{value});
691    }
692    @vals
693}
694
695sub vote_ballot_values {
696    my ($self, $voteid) = @_;
697
698    my $getval = $self->db->prepare_cached(
699        q{
700        select coalesce(corrected, value) as value from ballot join ballot_item
701        on ballot.id = ballot_item.id
702        where poll = ?
703        group by coalesce(corrected, value) order by coalesce(corrected, value)
704        }
705    );
706    $getval->execute($voteid);
707    my @vals;
708    while (my $res = $getval->fetchrow_hashref) {
709        push(@vals, $res->{value});
710    }
711    @vals
712}
713
714sub vote_map_value {
715    my ($self, $voteid, $from, $to) = @_;
716
717    my $sth = $self->db->prepare_cached(
718        q{
719        update ballot_item set corrected = ? where
720        id in (select id from ballot where poll = ?)
721        and value = ?
722        }
723    );
724
725    $sth->execute($to, $voteid, $from) or $self->db->rollback;
726    $self->db->commit;
727}
728
729sub addupd_voting {
730    my ($self, $voteid, $mail, $id) = @_;
731
732    my $upd = $self->db->prepare_cached(
733        q{
734        update voting set label = ? where mail = ? and poll = ?
735        }
736    );
737
738    if ($upd->execute($id || '', $mail, $voteid) == 0) {
739        my $add = $self->db->prepare_cached(q{
740            insert into voting (poll, label, mail) values (?,?,?)
741        });
742
743        $add->execute($voteid, $id || '', $mail);
744    }
745}
746
747sub delete_voting {
748    my ($self, $key) = @_;
749
750    $self->voting_has_sign($key) and return;
751    my $sth = $self->db->prepare_cached(
752        q{delete from voting where key = ?}
753    );
754
755    $sth->execute($key);
756}
757
758sub voting_from_file {
759    my ($self, $voteid, $fh, $delete) = @_;
760
761    if ($delete) {
762        my $sth = $self->db->prepare(q{delete from voting where poll = ?});
763        $sth->execute($voteid);
764    }
765
766    while (my $line = <$fh>) {
767        chomp($line);
768        my ($mail, $name) = split(';', $line);
769        $mail or do {
770            $self->db->rollback;
771            return;
772        };
773        $self->addupd_voting($voteid, $mail, $name || '');
774    }
775    1;
776}
777
778sub mail_passwd_ifnul {
779    my ($self, $voteid, $mailinfo) = @_;
780
781    my $list_voting = $self->db->prepare_cached(
782        q{select key from voting where poll = ? and passwd is null or passwd = ''}
783    );
784
785    $list_voting->execute($voteid);
786    while (my $res = $list_voting->fetchrow_hashref) {
787        $self->mail_voting_passwd($res->{key}, $mailinfo);
788    }
789}
790
791sub mail_voting_passwd {
792    my ($self, $id, $mailinfo) = @_;
793   
794    my $vinfo = $self->voting_info($id) or return;
795    my $voteinfo = $self->vote_info($vinfo->{poll});
796
797    my $passwd = random_string(8);
798    my $encpasswd = $self->gen_enc_passwd($passwd);
799
800    my $upd_voting = $self->db->prepare_cached(
801        q{update voting set passwd = ? where key = ?}
802    );
803
804    $upd_voting->execute($encpasswd, $id);
805
806    my $date = $voteinfo->{dstart} && $voteinfo->{dend}
807        ? sprintf("\n" . 'Vous pourrez voter entre le %s %s et le %s %s' . "\n",
808            $voteinfo->{dstart}, $voteinfo->{hstart}, $voteinfo->{dend}, $voteinfo->{hend})
809        : '';
810
811    # TODO complete this properly:
812    my $mailer = new Mail::Mailer 'smtp', Server => (Vote->config->{smtp} || 'localhost');
813    $mailer->open({
814        From => $voteinfo->{owner},
815        To => $vinfo->{mail},
816        Subject => 'Invitation a voter: ' . $voteinfo->{label},
817        'X-Epoll-poll' => $id,
818        mail_header(),
819    });
820    print $mailer <<EOF;
821Vous êtes convié à participer a ce vote:
822
823--------
824$voteinfo->{label}
825--------
826
827à l'adresse:
828
829$mailinfo->{voteurl}
830$date
831Votre identifiant est: $vinfo->{mail}
832Votre mot de passe est: $passwd
833
834Conserver précieusement ces identifiants, il ne vous seront pas retransmit.
835
836Cordialement.
837EOF
838    $mailer->close or warn "couldn't send whole message: $!\n";
839
840    $self->db->commit;
841}
842
843sub poll_request_info {
844    my ($self, $rid) = @_;
845
846    my $sth = $self->db->prepare_cached(
847        q{select * from poll_request where id = ?}
848    );
849
850    $sth->execute($rid);
851    my $res = $sth->fetchrow_hashref;
852    $sth->finish;
853    $res
854}
855
856sub poll_from_request {
857    my ($self, $rid, $passwd) = @_;
858    my $rinfo = $self->poll_request_info($rid) or return;
859
860    my $encpasswd = $self->gen_enc_passwd($passwd);
861
862    my $getpollid = $self->db->prepare_cached(
863        q{select nextval('poll_id_seq')}
864    );
865    $getpollid->execute();
866    my $newpollid = $getpollid->fetchrow_hashref->{nextval};
867   
868    my $newpoll = $self->db->prepare_cached(
869        q{insert into poll (id, label, owner, password) values (?,?,?,?)}
870    );
871
872    $newpoll->execute($newpollid, $rinfo->{label}, $rinfo->{mail}, $encpasswd);
873    # set some default
874    $self->vote_param($newpollid,
875        free_choice => 0,
876        choice_count => 1,
877    );     
878
879    my $delreq = $self->db->prepare_cached(
880        q{delete from poll_request where id = ?}
881    );
882
883    $delreq->execute($rid);
884    $self->db->commit;
885
886    $newpollid
887}
888
889sub create_poll_request {
890    my ($self, %info) = @_;
891
892    $info{mail} or return;
893    my $addreq = $self->db->prepare_cached(
894        q{insert into poll_request (id, label, mail) values (?,?,?)}
895    );
896
897    my $reqid = gen_uid;
898
899    $addreq->execute($reqid, $info{label}, $info{mail});
900    my $mailer = new Mail::Mailer 'smtp', Server => (Vote->config->{smtp} || 'localhost');
901    $mailer->open({
902        From => 'Voting system <nomail@nomail.com>', # TODO allow to configure this
903        To => $info{mail},
904        Subject => 'Votre nouveau vote',
905        mail_header(),
906    });
907    print $mailer <<EOF;
908
909Vous avez demandez la création d'un nouveau vote:
910$info{label}
911
912Pour valider votre demande, veuiller allez visitez la page:
913$info{url}/$reqid
914
915A bientÃŽt
916EOF
917    $mailer->close
918        or warn "couldn't send whole message: $!\n";
919    $self->db->commit;
920    1;
921}
922
923=head1 AUTHOR
924
925Thauvin Olivier
926
927=head1 LICENSE
928
929This library is free software, you can redistribute it and/or modify
930it under the same terms as Perl itself.
931
932=cut
933
9341;
Note: See TracBrowser for help on using the repository browser.