#!/usr/bin/perl # package LJ::Poll; use strict; use HTML::TokeParser (); require "$ENV{'LJHOME'}/cgi-bin/cleanhtml.pl"; require "$ENV{'LJHOME'}/cgi-bin/LJR/ljpoll-local.pl"; sub clean_poll { my $ref = shift; if ($$ref !~ /[<>]/) { LJ::text_out($ref); return; } my $poll_eat = [qw[head title style layer iframe applet object]]; my $poll_allow = [qw[a b i u strong em img]]; my $poll_remove = [qw[bgsound embed object caption link font]]; LJ::CleanHTML::clean($ref, { 'wordlength' => 40, 'addbreaks' => 0, 'eat' => $poll_eat, 'mode' => 'deny', 'allow' => $poll_allow, 'remove' => $poll_remove, }); LJ::text_out($ref); } sub contains_new_poll { my $postref = shift; return ($$postref =~ /{'posterid'} += 0; $iteminfo->{'journalid'} += 0; my $newdata; my $popen = 0; my %popts; my $qopen = 0; my %qopts; my $iopen = 0; my %iopts; my @polls; # completed parsed polls my $p = HTML::TokeParser->new($postref); # if we're being called from mailgated, then we're not in web context and therefore # do not have any BML::ml functionality. detect this now and report errors in a # plaintext, non-translated form to be bounced via email. my $have_bml = eval { BML::ml() } || ! $@; my $err = sub { # more than one element, either make a call to BML::ml # or build up a semi-useful error string from it if (@_ > 1) { if ($have_bml) { $$error = BML::ml(@_); return 0; } $$error = shift() . ": "; while (my ($k, $v) = each %{$_[0]}) { $$error .= "$k=$v,"; } chop $$error; return 0; } # single element, either look up in %BML::ML or return verbatim $$error = $have_bml ? $BML::ML{$_[0]} : $_[0]; return 0; }; while (my $token = $p->get_token) { my $type = $token->[0]; my $append; if ($type eq "S") # start tag { my $tag = $token->[1]; my $opts = $token->[2]; ######## Begin poll tag if ($tag eq "lj-poll") { return $err->('poll.error.nested', { 'tag' => 'lj-poll' }) if $popen; $popen = 1; %popts = (); $popts{'questions'} = []; $popts{'name'} = $opts->{'name'}; $popts{'whovote'} = lc($opts->{'whovote'}) || "all"; $popts{'whoview'} = lc($opts->{'whoview'}) || "all"; if ($popts{'whovote'} ne "all" && $popts{'whovote'} ne "friends") { return $err->('poll.error.whovote'); } if ($popts{'whoview'} ne "all" && $popts{'whoview'} ne "friends" && $popts{'whoview'} ne "none") { return $err->('poll.error.whoview'); } } ######## Begin poll question tag elsif ($tag eq "lj-pq") { return $err->('poll.error.nested', { 'tag' => 'lj-pq' }) if $qopen; return $err->('poll.error.missingljpoll') unless $popen; $qopen = 1; %qopts = (); $qopts{'items'} = []; $qopts{'type'} = $opts->{'type'}; if ($qopts{'type'} eq "text") { my $size = 35; my $max = 255; if (defined $opts->{'size'}) { if ($opts->{'size'} > 0 && $opts->{'size'} <= 100) { $size = $opts->{'size'}+0; } else { return $err->('poll.error.badsize'); } } if (defined $opts->{'maxlength'}) { if ($opts->{'maxlength'} > 0 && $opts->{'maxlength'} <= 255) { $max = $opts->{'maxlength'}+0; } else { return $err->('poll.error.badmaxlength'); } } $qopts{'opts'} = "$size/$max"; } if ($qopts{'type'} eq "scale") { my $from = 1; my $to = 10; my $by = 1; if (defined $opts->{'from'}) { $from = int($opts->{'from'}); } if (defined $opts->{'to'}) { $to = int($opts->{'to'}); } if (defined $opts->{'by'}) { $by = int($opts->{'by'}); } if ($by < 1) { return $err->('poll.error.scaleincrement'); } if ($from >= $to) { return $err->('poll.error.scalelessto'); } if ((($to-$from)/$by) > 20) { return $err->('poll.error.scaletoobig'); } $qopts{'opts'} = "$from/$to/$by"; } $qopts{'type'} = lc($opts->{'type'}) || "text"; if ($qopts{'type'} ne "radio" && $qopts{'type'} ne "check" && $qopts{'type'} ne "drop" && $qopts{'type'} ne "scale" && $qopts{'type'} ne "text") { return $err->('poll.error.unknownpqtype'); } } ######## Begin poll item tag elsif ($tag eq "lj-pi") { if ($iopen) { return $err->('poll.error.nested', { 'tag' => 'lj-pi' }); } if (! $qopen) { return $err->('poll.error.missingljpq'); } if ($qopts{'type'} eq "text") { return $err->('poll.error.noitemstext'); } $iopen = 1; %iopts = (); } #### not a special tag. dump it right back out. else { $append .= "<$tag"; foreach (keys %$opts) { $append .= " $_=\"$opts->{$_}\""; } $append .= ">"; } } elsif ($type eq "E") { my $tag = $token->[1]; ##### end POLL if ($tag eq "lj-poll") { return $err->('poll.error.tagnotopen', { 'tag' => 'lj-poll' }) unless $popen; $popen = 0; return $err->('poll.error.noquestions') unless @{$popts{'questions'}}; $popts{'journalid'} = $iteminfo->{'journalid'}; $popts{'posterid'} = $iteminfo->{'posterid'}; push @polls, { %popts }; $append .= ""; } ##### end QUESTION elsif ($tag eq "lj-pq") { return $err->('poll.error.tagnotopen', { 'tag' => 'lj-pq' }) unless $qopen; unless ($qopts{'type'} eq "scale" || $qopts{'type'} eq "text" || @{$qopts{'items'}}) { return $err->('poll.error.noitems'); } $qopts{'qtext'} =~ s/^\s+//; $qopts{'qtext'} =~ s/\s+$//; my $len = length($qopts{'qtext'}) or return $err->('poll.error.notext'); push @{$popts{'questions'}}, { %qopts }; $qopen = 0; } ##### end ITEM elsif ($tag eq "lj-pi") { return $err->('poll.error.tagnotopen', { 'tag' => 'lj-pi' }) unless $iopen; $iopts{'item'} =~ s/^\s+//; $iopts{'item'} =~ s/\s+$//; my $len = length($iopts{'item'}); return $err->('poll.error.pitoolong', { 'len' => $len, }) if $len > 255 || $len < 1; push @{$qopts{'items'}}, { %iopts }; $iopen = 0; } ###### not a special tag. else { $append .= ""; } } elsif ($type eq "T" || $type eq "D") { $append = $token->[1]; } elsif ($type eq "C") { # ignore comments } elsif ($type eq "PI") { $newdata .= "[1]>"; } else { $newdata .= "\n"; } ##### append stuff to the right place if (length($append)) { if ($iopen) { $iopts{'item'} .= $append; } elsif ($qopen) { $qopts{'qtext'} .= $append; } elsif ($popen) { 0; # do nothing. } else { $newdata .= $append; } } } if ($popen) { return $err->('poll.error.unlockedtag', { 'tag' => 'lj-poll' }); } if ($qopen) { return $err->('poll.error.unlockedtag', { 'tag' => 'lj-pq' }); } if ($iopen) { return $err->('poll.error.unlockedtag', { 'tag' => 'lj-pi' }); } $$postref = $newdata; return @polls; } # preview poll # -- accepts $poll hashref as found in the array returned by LJ::Poll::parse() sub preview { my $poll = shift; return unless ref $poll eq 'HASH'; my $ret = ''; $ret .= "
\n"; $ret .= "" . BML::ml('poll.pollnum', { 'num' => 'xxxx' }) . ""; if ($poll->{'name'}) { LJ::Poll::clean_poll(\$poll->{'name'}); $ret .= " $poll->{'name'}"; } $ret .= "
\n"; $ret .= BML::ml('poll.security', { 'whovote' => $BML::ML{'poll.security.'.$poll->{whovote}}, 'whoview' => $BML::ML{'poll.security.'.$poll->{whoview}}, }); # iterate through all questions foreach my $q (@{$poll->{'questions'}}) { if ($q->{'qtext'}) { LJ::Poll::clean_poll(\$q->{'qtext'}); $ret .= "

$q->{'qtext'}

\n"; } $ret .= "
"; # text questions if ($q->{'type'} eq 'text') { my ($size, $max) = split(m!/!, $q->{'opts'}); $ret .= LJ::html_text({ 'size' => $size, 'maxlength' => $max }); # scale questions } elsif ($q->{'type'} eq 'scale') { my ($from, $to, $by) = split(m!/!, $q->{'opts'}); $by ||= 1; my $count = int(($to-$from)/$by) + 1; my $do_radios = ($count <= 11); # few opts, display radios if ($do_radios) { $ret .= "\n"; for (my $at = $from; $at <= $to; $at += $by) { $ret .= "\n"; } $ret .= "
" . LJ::html_check({ 'type' => 'radio' }) . "
$at
\n"; # many opts, display select } else { my @optlist = (); for (my $at = $from; $at <= $to; $at += $by) { push @optlist, ('', $at); } $ret .= LJ::html_select({}, @optlist); } # questions with items } else { # drop-down list if ($q->{'type'} eq 'drop') { my @optlist = ('', ''); foreach my $it (@{$q->{'items'}}) { LJ::Poll::clean_poll(\$it->{'item'}); push @optlist, ('', $it->{'item'}); } $ret .= LJ::html_select({}, @optlist); # radio or checkbox } else { foreach my $it (@{$q->{'items'}}) { LJ::Poll::clean_poll(\$it->{'item'}); $ret .= LJ::html_check({ 'type' => $q->{'type'} }) . "$it->{'item'}
\n"; } } } $ret .= "
\n"; } $ret .= LJ::html_submit('', $BML::ML{'poll.submit'}, { 'disabled' => 1 }) . "\n"; $ret .= "
"; return $ret; } # note: $itemid is a $ditemid (display itemid, *256 + anum) sub register { &LJ::nodb; my $dbh = LJ::get_db_writer(); my $post = shift; my $error = shift; my $itemid = shift; my @polls = @_; foreach my $po (@polls) { my %popts = %$po; $popts{'itemid'} = $itemid+0; #### CREATE THE POLL! my $sth = $dbh->prepare("INSERT INTO poll (itemid, journalid, posterid, whovote, whoview, name) " . "VALUES (?, ?, ?, ?, ?, ?)"); $sth->execute($itemid, $popts{'journalid'}, $popts{'posterid'}, $popts{'whovote'}, $popts{'whoview'}, $popts{'name'}); if ($dbh->err) { $$error = BML::ml('poll.dberror', { errmsg => $dbh->errstr }); return 0; } my $pollid = $dbh->{'mysql_insertid'}; $$post =~ s///; # NOT global replace! ## start inserting poll questions my $qnum = 0; foreach my $q (@{$popts{'questions'}}) { $qnum++; $sth = $dbh->prepare("INSERT INTO pollquestion (pollid, pollqid, sortorder, type, opts, qtext) " . "VALUES (?, ?, ?, ?, ?, ?)"); $sth->execute($pollid, $qnum, $qnum, $q->{'type'}, $q->{'opts'}, $q->{'qtext'}); if ($dbh->err) { $$error = BML::ml('poll.dberror.questions', { errmsg => $dbh->errstr }); return 0; } my $pollqid = $dbh->{'mysql_insertid'}; ## start inserting poll items my $inum = 0; foreach my $it (@{$q->{'items'}}) { $inum++; $dbh->do("INSERT INTO pollitem (pollid, pollqid, pollitid, sortorder, item) " . "VALUES (?, ?, ?, ?, ?)", undef, $pollid, $qnum, $inum, $inum, $it->{'item'}); if ($dbh->err) { $$error = BML::ml('poll.dberror.items', { errmsg => $dbh->errstr }); return 0; } } ## end inserting poll items } ## end inserting poll questions } ### end while over all poles } sub show_polls { &LJ::nodb; my $itemid = shift; my $remote = shift; my $postref = shift; $$postref =~ s//&show_poll($itemid, $remote, $1)/eg; } sub show_poll { &LJ::nodb; my $dbr = LJ::get_db_reader(); my $itemid = shift; my $remote = shift; my $pollid = shift; my $opts = shift; # hashref. {"mode" => results/enter/ans} my $sth; my $mode = $opts->{'mode'}; $pollid += 0; my $po = $dbr->selectrow_hashref("SELECT * FROM poll WHERE pollid=?", undef, $pollid); return "[" . BML::ml('poll.error.pollnotfound', { 'num' => $pollid }) . "]" unless $po; return "[$BML::ML{'poll.error.noentry'}]" if $itemid && $po->{'itemid'} != $itemid; my ($can_vote, $can_view) = find_security($po, $remote); # update the mode if we need to $mode = 'results' unless $remote; if (!$mode && $remote) { my $time = $dbr->selectrow_array('SELECT datesubmit FROM pollsubmission '. 'WHERE pollid=? AND userid=?', undef, $pollid, $remote->{userid}); $mode = $time ? 'results' : $can_vote ? 'enter' : 'results'; } ### load all the questions my @qs; $sth = $dbr->prepare('SELECT * FROM pollquestion WHERE pollid=?'); $sth->execute($pollid); push @qs, $_ while $_ = $sth->fetchrow_hashref; @qs = sort { $a->{sortorder} <=> $b->{sortorder} } @qs; ### load all the items my %its; $sth = $dbr->prepare("SELECT pollqid, pollitid, item FROM pollitem WHERE pollid=? ORDER BY sortorder"); $sth->execute($pollid); while (my ($qid, $itid, $item) = $sth->fetchrow_array) { push @{$its{$qid}}, [ $itid, $item ]; } # see if we have a hook for alternate poll contents my $ret = LJ::run_hook('alternate_show_poll_html', $po, $mode, \@qs); return $ret if $ret; ### view answers to a particular question in a poll if ($mode eq "ans") { return "[$BML::ML{'poll.error.cantview'}]" unless $can_view; # get the question from @qs, which we loaded earlier my $q; foreach (@qs) { $q = $_ if $_->{pollqid} == $opts->{qid}; } return "[$BML::ML{'poll.error.questionnotfound'}]" unless $q; # get the item information from %its, also loaded earlier my %it; $it{$_->[0]} = $_->[1] foreach (@{$its{$opts->{qid}}}); LJ::Poll::clean_poll(\$q->{'qtext'}); $ret .= $q->{'qtext'}; $ret .= "

"; my $LIMIT = 2000; $sth = $dbr->prepare("SELECT u.user, pr.value, ps.datesubmit ". "FROM useridmap u, pollresult pr, pollsubmission ps " . "WHERE u.userid=pr.userid AND pr.pollid=? AND pollqid=? " . "AND ps.pollid=pr.pollid AND ps.userid=pr.userid LIMIT $LIMIT"); $sth->execute($pollid, $opts->{'qid'}); my @res; push @res, $_ while $_ = $sth->fetchrow_hashref; @res = sort { $a->{datesubmit} cmp $b->{datesubmit} } @res; foreach my $res (@res) { my ($user, $value) = ($res->{user}, $res->{value}); ## some question types need translation; type 'text' doesn't. if ($q->{'type'} eq "radio" || $q->{'type'} eq "drop") { $value = $it{$value}; } elsif ($q->{'type'} eq "check") { $value = join(", ", map { $it{$_} } split(/,/, $value)); } LJ::Poll::clean_poll(\$value); $ret .= "

" . LJ::ljuser($user) . " -- $value

\n"; } # temporary if (@res == $LIMIT) { $ret .= "

[$BML::ML{'poll.error.truncated'}]

"; } return $ret; } # Users cannot vote unless they are logged in return "" if $mode eq 'enter' && !$remote; my $do_form = $mode eq 'enter' && $can_vote; my %preval; if ($do_form) { $sth = $dbr->prepare("SELECT pollqid, value FROM pollresult WHERE pollid=? AND userid=?"); $sth->execute($pollid, $remote->{'userid'}); while (my ($qid, $value) = $sth->fetchrow_array) { $preval{$qid} = $value; } $ret .= "
"; $ret .= LJ::form_auth(); $ret .= LJ::html_hidden('pollid', $pollid); } $ret .= "" . BML::ml('poll.pollnum', { 'num' => $pollid }) . " "; if ($po->{'name'}) { LJ::Poll::clean_poll(\$po->{'name'}); $ret .= "$po->{'name'}"; } $ret .= "
\n"; $ret .= BML::ml('poll.security', { 'whovote' => $BML::ML{'poll.security.'.$po->{whovote}}, 'whoview' => $BML::ML{'poll.security.'.$po->{whoview}} }); my $text = LJ::run_hook('extra_poll_description', $po, \@qs); $ret .= "
$text" if $text; ## go through all questions, adding to buffer to return foreach my $q (@qs) { my $qid = $q->{'pollqid'}; LJ::Poll::clean_poll(\$q->{'qtext'}); $ret .= "

$q->{'qtext'}

"; ### get statistics, for scale questions my ($valcount, $valmean, $valstddev, $valmedian); if ($q->{'type'} eq "scale") { ## manually add all the possible values, since they aren't in the database ## (which was the whole point of making a "scale" type): my ($from, $to, $by) = split(m!/!, $q->{'opts'}); $by = 1 unless ($by > 0 and int($by) == $by); for (my $at=$from; $at<=$to; $at+=$by) { push @{$its{$qid}}, [ $at, $at ]; # note: fake itemid, doesn't matter, but needed to be unique } $sth = $dbr->prepare("SELECT COUNT(*), AVG(value), STDDEV(value) FROM pollresult WHERE pollid=? AND pollqid=?"); $sth->execute($pollid, $qid); ($valcount, $valmean, $valstddev) = $sth->fetchrow_array; # find median: $valmedian = 0; if ($valcount == 1) { $valmedian = $valmean; } elsif ($valcount > 1) { my ($mid, $fetch); # fetch two mids and average if even count, else grab absolute middle $fetch = ($valcount % 2) ? 1 : 2; $mid = int(($valcount+1)/2); my $skip = $mid-1; $sth = $dbr->prepare("SELECT value FROM pollresult WHERE pollid=? AND pollqid=? " . "ORDER BY value+0 LIMIT $skip,$fetch"); $sth->execute($pollid, $qid); while (my ($v) = $sth->fetchrow_array) { $valmedian += $v; } $valmedian /= $fetch; } } my $usersvoted = 0; my %itvotes; my $maxitvotes = 1; if ($mode eq "results") { ### to see individual's answers $ret .= "$BML::ML{'poll.viewanswers'}
"; ### but, if this is a non-text item, and we're showing results, need to load the answers: if ($q->{'type'} ne "text") { $sth = $dbr->prepare("SELECT value FROM pollresult WHERE pollid=? AND pollqid=?"); $sth->execute($pollid, $qid); while (my ($val) = $sth->fetchrow_array) { $usersvoted++; if ($q->{'type'} eq "check") { foreach (split(/,/,$val)) { $itvotes{$_}++; } } else { $itvotes{$val}++; } } foreach (values %itvotes) { $maxitvotes = $_ if ($_ > $maxitvotes); } } } #### text questions are the easy case if ($q->{'type'} eq "text" && $do_form) { my ($size, $max) = split(m!/!, $q->{'opts'}); $ret .= LJ::html_text({ 'size' => $size, 'maxlength' => $max, 'name' => "pollq-$qid", 'value' => $preval{$qid} }); } #### drop-down list elsif ($q->{'type'} eq 'drop' && $do_form) { my @optlist = ('', ''); foreach my $it (@{$its{$qid}}) { my ($itid, $item) = @$it; LJ::Poll::clean_poll(\$item); push @optlist, ($itid, $item); } $ret .= LJ::html_select({ 'name' => "pollq-$qid", 'selected' => $preval{$qid} }, @optlist); } #### scales (from 1-10) questions elsif ($q->{'type'} eq "scale" && $do_form) { my ($from, $to, $by) = split(m!/!, $q->{'opts'}); $by ||= 1; my $count = int(($to-$from)/$by) + 1; my $do_radios = ($count <= 11); # few opts, display radios if ($do_radios) { $ret .= ""; for (my $at=$from; $at<=$to; $at+=$by) { $ret .= ""; } $ret .= "
"; $ret .= LJ::html_check({ 'type' => 'radio', 'name' => "pollq-$qid", 'value' => $at, 'id' => "pollq-$pollid-$qid-$at", 'selected' => (defined $preval{$qid} && $at == $preval{$qid}) }); $ret .= "
\n"; # many opts, display select # but only if displaying form } else { my @optlist = ('', ''); for (my $at=$from; $at<=$to; $at+=$by) { push @optlist, ($at, $at); } $ret .= LJ::html_select({ 'name' => "pollq-$qid", 'selected' => $preval{$qid} }, @optlist); } } #### now, questions with items else { my $do_table = 0; if ($q->{'type'} eq "scale") { # implies ! do_form my $stddev = sprintf("%.2f", $valstddev); my $mean = sprintf("%.2f", $valmean); $ret .= BML::ml('poll.scaleanswers', { 'mean' => $mean, 'median' => $valmedian, 'stddev' => $stddev }); $ret .= "
\n"; $do_table = 1; $ret .= ""; } foreach my $it (@{$its{$qid}}) { my ($itid, $item) = @$it; LJ::Poll::clean_poll(\$item); # displaying a radio or checkbox if ($do_form) { $ret .= LJ::html_check({ 'type' => $q->{'type'}, 'name' => "pollq-$qid", 'value' => $itid, 'id' => "pollq-$pollid-$qid-$itid", 'selected' => ($preval{$qid} =~ /\b$itid\b/) }); $ret .= "
"; next; } # displaying results my $count = $itvotes{$itid}+0; my $percent = sprintf("%.1f", (100 * $count / ($usersvoted||1))); my $width = 20+int(($count/$maxitvotes)*380); if ($do_table) { $ret .= ""; $ret .= ""; } else { $ret .= "

$item
"; $ret .= ""; $ret .= "$count ($percent%)"; $ret .= " "; $ret .= "$count ($percent%)

"; } } if ($do_table) { $ret .= "
$item"; $ret .= "$count ($percent%)"; $ret .= " "; $ret .= "$count ($percent%)
"; } } $ret .= "
"; } if ($do_form) { $ret .= LJ::html_submit('poll-submit', $BML::ML{'poll.submit'}) . "
\n";; } return $ret; } sub find_security { &LJ::nodb; my $po = shift; my $remote = shift; my $sth; ## if remote is poll owner, can do anything. if ($remote && $remote->{'userid'} == $po->{'posterid'}) { return (1, 1); } ## need to be both a person and with a visible journal to vote if ($remote && ($remote->{'journaltype'} ne "P" || $remote->{'statusvis'} ne "V")) { return (0, 0); } my $is_friend = 0; if (($po->{'whoview'} eq "friends" || $po->{'whovote'} eq "friends") && $remote) { $is_friend = LJ::is_friend($po->{'journalid'}, $remote->{'userid'}); } my %sec; if ($po->{'whoview'} eq "all" || ($po->{'whoview'} eq "friends" && $is_friend) || ($po->{'whoview'} eq "none" && $remote && $remote->{'userid'} == $po->{'posterid'})) { $sec{'view'} = 1; } if ($po->{'whovote'} eq "all" || ($po->{'whovote'} eq "friends" && $is_friend)) { $sec{'vote'} = 1; } if ($sec{'vote'} && (LJ::is_banned($remote, $po->{'journalid'}) || LJ::is_banned($remote, $po->{'posterid'}))) { $sec{'vote'} = 0; } return ($sec{'vote'}, $sec{'view'}); } sub submit { &LJ::nodb; my $remote = shift; my $form = shift; my $error = shift; my $sth; my $dbh = LJ::get_db_writer(); unless ($remote) { $$error = $BML::ML{'error.noremote'}; # instead of , because errors are displayed in LJ::bad_input() return 0; } my $pollid = $form->{'pollid'}+0; my $po = $dbh->selectrow_hashref("SELECT itemid, whovote, journalid, posterid, whoview, whovote, name ". "FROM poll WHERE pollid=?", undef, $pollid); unless ($po) { $$error = $BML::ML{'poll.error.nopollid'}; return 0; } my ($can_vote, undef) = find_security($po, $remote); unless ($can_vote) { $$error = $BML::ML{'poll.error.cantvote'}; return 0; } ### load all the questions my @qs; $sth = $dbh->prepare("SELECT pollqid, type, opts, qtext FROM pollquestion WHERE pollid=?"); $sth->execute($pollid); push @qs, $_ while $_ = $sth->fetchrow_hashref; foreach my $q (@qs) { my $qid = $q->{'pollqid'}+0; my $val = $form->{"pollq-$qid"}; if ($q->{'type'} eq "check") { ## multi-selected items are comma separated from htdocs/poll/index.bml $val = join(",", sort { $a <=> $b } split(/,/, $val)); } if ($q->{'type'} eq "scale") { my ($from, $to, $by) = split(m!/!, $q->{'opts'}); if ($val < $from || $val > $to) { # bogus! cheating? $val = ""; } } if ($val ne "") { $dbh->do("REPLACE INTO pollresult (pollid, pollqid, userid, value) VALUES (?, ?, ?, ?)", undef, $pollid, $qid, $remote->{'userid'}, $val); } else { $dbh->do("DELETE FROM pollresult WHERE pollid=? AND pollqid=? AND userid=?", undef, $pollid, $qid, $remote->{'userid'}); } } ## finally, register the vote happened $dbh->do("REPLACE INTO pollsubmission (pollid, userid, datesubmit) VALUES (?, ?, NOW())", undef, $pollid, $remote->{'userid'}); return 1; } 1;