;
}
eval $txt;
die "couldn't load closed poll data: $@" if $@;
return $VAR1;
}
open(P,$blosxom::datadir."/".$dir."/".$pname.".txt") ||die "can't read poll file $blosxom::datadir/$dir/$pname.txt";
my @poll=;
close(P);
my $poll= $pkg->_int_parse($dir,$pname,\@poll,@files);
return $poll;
}
sub _int_parse{
my ($pkg,$dir,$pname,$lines,@files)=@_;
return $parsed{$dir."/".$pname} if $parsed{$dir."/".$pname};
my %poll;
my @poll = @$lines;
my $header=shift @poll;
my (@qs,$rest);
chomp($header);
$poll{'head'}=$header;
shift @poll while $poll[0] =~/^\s*$/s and @poll;
while($poll[0]=~/^(\S.*?)\s+(\S.*)$/s and @poll){
my $a=$2;
chomp($a);
push @qs,$a;
shift @poll;
}
$poll{'items'}=\@qs;
shift @poll while $poll[0] =~/^\s*$/s and @poll;
$rest = join("\n",@poll);
$poll{'foot'}=$rest if $rest;
if(!$poll{'head'} || (@qs<1) ){
return undef;
}
#parse results
$poll{'results'}= {total=>{map {$_=>0}0..@qs-1},unique=>{}};
my $tot=0;
my $max=0;
$poll->{'results'}->{'winner'}=0;
if(-r $blosxom::plugin_state_dir."/".$dir."/".$pname.".voted"){
open(V,$blosxom::plugin_state_dir."/".$dir."/".$pname.".voted") || die 'unable to open tallied votes for counting';
for(){
chomp;
if(/^(.*?):(\d+)/){
my $v=$2;
my $u=$1;
$poll{'results'}->{'total'}->{$v}++;
$poll{'results'}->{'unique'}->{$u}=1;
$tot++;
$poll{'results'}->{'winner'} = $poll{'results'}->{'total'}->{$v}
if $poll{'results'}->{'total'}->{$v} >$poll{'results'}->{'winner'};
}
}
close(V);
}
my $vfcount=0;
foreach (@files){
if(/^$pname\.(.*?)\.vote\.(\d+)$/){
my $v=$2;
my $u=$1;
$poll{'results'}->{'total'}->{$v}++;
$poll{'results'}->{'unique'}->{$u}=1;
$tot++;
$poll{'results'}->{'winner'} = $poll{'results'}->{'total'}->{$v}
if $poll{'results'}->{'total'}->{$v} >$poll{'results'}->{'winner'};
$vfcount++;
}
}
if ( -e $blosxom::plugin_state_dir."/".$dir."/".$pname.".closed" ){
$poll{'closed'}=1;
}
$poll{'results'}->{'totalvotes'}=$tot;
$poll{'dir'}=$dir;
$poll{'name'}=$pname;
$poll{'files'}=\@files;
$parsed{$dir."/".$pname}=\%poll;
if($vfcount > $poll_max_unconsolidated){
if($pkg->_lock_poll($dir,$pname)){
$pkg->consolidate($dir,$pname,@files);
$pkg->_unlock_poll($dir,$pname);
}
}
return \%poll;
}
sub _lock_poll{
my ($pkg,$dir,$pname)=@_;
my $md="$blosxom::plugin_state_dir/$dir";
unless(-d $md){
$pkg->_make_dir($md);
}
if($flocks{$dir."/".$pname}){
flock($flocks{$dir."/".$pname},LOCK_EX); #wait...
return 1;
}else{
open(X,">>$md/$pname.poll.lock")
or (warn "Unable to open lock for writing: $!" and return 0);
flock(X,LOCK_EX); #wait...
$flocks{$dir."/".$pname}=*X{IO};
return 1;
}
}
sub _unlock_poll{
my ($pkg,$dir,$pname)=@_;
my $fh = $flocks{$dir."/".$pname};
return unless $fh;
flock($fh,LOCK_UN);
close($fh);
}
sub gen_poll{
my ($pkg,$name,$poll,$msg)=@_;
my (@qs,$rest);
my $html;
$html.=qq(
$poll_head
};
$html.=$poll_foot_head.$poll->{'foot'}.$poll_foot_foot
if $poll->{'foot'};
$html.=$poll_foot;
return $html;
}
sub default_output{
my ($name, $choicenum, $perc, $count, $totalvotes,$winner,$head,$foot) =@_;
my $pc = sprintf("%.1f",$perc);
return qq{
$head $name - $pc% $foot};
}
sub gen_poll_results{
my ($pkg,$poll,$msg) = @_;
my $phead = $poll->{'templated'}? $poll_cur_head : $poll_head;
my $pfoot = $poll->{'templated'} ? $poll_cur_foot : $poll_foot;
my $html;
$html.=qq(
$phead
$poll_head_head $poll->{'head'} $poll_head_foot
$poll_list_head
);
#warn "generating, total votes:".$poll->{'results'}->{'totalvotes'}." winner:".$poll->{'results'}->{'winner'};
foreach my $x (0..@{$poll->{'items'}}-1){
my $count = $poll->{'results'}->{'total'}->{$x};
my $pc = ($count > 0 ? (($count / $poll->{'results'}->{'totalvotes'}) *100) : 0);
#warn "vote item $x : count $count, % $pc, perc = $perc";
my $win = $poll->{'results'}->{'winner'}== $count ;
my $out;
eval{ no strict 'refs';
$out = &$poll_item_output_sub($poll->{'items'}->[$x], $x, $pc, $count, $pol->{'results'}->{'totalvotes'},$win,($win? $poll_item_win_head: $poll_item_res_head ),($win ?$poll_item_win_foot: $poll_item_res_foot));
};
if($@){
warn "Error calling \$poll_item_output_sub:$@";
return "There was an error generating poll results.";
}
$html.= $out;
}
$html.=qq(
$poll_list_foot
$poll_msg_head).
qq(Total Votes: $poll->{'results'}->{'totalvotes'});
$html.=qq(
Voting closed on $poll->{'dateclosed'})
if $poll->{'dateclosed'};
$html.=qq(
$msg) if $msg;
$html.= $poll_msg_foot;
$html.=($poll->{'foot'}? '
'.$poll_foot_head .$poll->{'foot'}.$poll_foot_foot :'');
$html.=$pfoot;
return $html;
}
1;
__END__
=head1 NAME
Blosxom Plug-in: poll
=head1 SYNOPSIS
Purpose: Provides public polls which can be placed in the header or footer templates,
or viewed as blosxom entries.
Minimal configuration is necessary, but plenty of customization is possible.
$poll::html will contain the HTML for the "current" poll,
which is the newest poll in the special I, although many polls may be
active at one time.
=head1 QUICK START
Edit the poll plugin file and make sure these are set correctly:
# a blosxom dir to contain only polls.
$poll_dir = '/polls';
# if any blosxom entry contains this in the name, it will act like a poll
$poll_marker = "poll-";
# set this password to something secret, so you can close polls
$poll_admin_password = 'secret';
Make sure your webserver can write to the plugin_state_dir (which you set in blosxom.cgi).
Then, create a poll file entry, either in the polls_dir, or with a name containing the poll_marker:
What is your favorite cheese?
* Gouda
* Brie
* Cheddar
* Parmesan
* Goat
* Cheese is gross!
If anybody votes 'Cheese is gross!', they're insane!
The first line is the title, the items come after a blank line, and anything after the last item is shown at the bottom.
The name for each item is everything after the first word, (so "*" is left out in this case).
The newest poll in the poll_dir is the "default" or "current" poll. If you want to include the default poll
in the blosxom header.html or footer.html put "$poll::html" in your template somewhere.
Simple!
It's ok to edit your poll files once they are created, (e.g. typos)
I.
=head1 QUICK ADMIN
Go to any page of your blog, and add C to the URL, and hit enter.
This will close the current poll so nobody can vote on it anymore. (change "secret" to
the password you configured above.)
To close a specific poll, go to a page containing the poll and add C
to the URL and hit enter. Make sure the "pollname" has the correct blosxom path and name for the poll entry.
=head1 BIT MORE SYNOPSIS
The generated HTML is customizable with various configuration options. (see below)
Client IP addresses/hostnames are used to try to maintain a 1 vote per client rule.
Polls can be B at any time (see L"Closing A Poll"> below), which writes
the results to a final form and prevents further voting.
When a poll is interpreted, such as in a blosxom entry, or in $poll::html, it
will contain either the HTML for voting (HTML form) or the results of the poll,
depending on whether the poll is still open and whether the client has already voted.
Polls are created by simple text files (in the blosxom spirit). All entries in the special
I are interpreted as polls, and any entry in any other directory can
be interpreted as a poll if the name of the entry contains the poll marker (default "poll-").
=head1 DETAILS
=over
=item Configuration
These configuration values should be set in the 'poll' plugin file.
$poll_dir is the path relative to $blosxom::datadir where polls are stored.
$poll_dir = '/polls';
$poll_marker is a string which blosxom entries must have in their name if they
are to be parsed as polls but are not in the polls directory. Set to "" if you want to disable.
$poll_marker = "poll-";
The admin password is used to allow an admin to remotely close a vote. (See L below).
$poll_admin_password = 'secret';
Text that is wrapped around the entire poll HTML of the current poll in headers/footers.
$poll_cur_head='';
$poll_cur_foot='
';
Text that is wrapped around the entire poll HTML.
$poll_head='';
$poll_foot='
';
Text that is rapped around the Title of the poll.
$poll_head_head='';
$poll_head_foot='
';
Text that is wrapped around the name of the poll item when part of a form.
$poll_item_form_head='';
$poll_item_form_foot='
';
Text that is wrapped around the name of the poll item when shown in the results.
$poll_item_res_head='';
$poll_item_res_foot='
';
Text that is wrapped around the winning poll item(s) when shown in the results.
$poll_item_win_head='';
$poll_item_win_foot='
';
Text that is wrapped around the footer of the poll (from the poll file).
$poll_foot_head='
';
Text that is wrapped around any messages for the poll (errors, warnings).
$poll_msg_head='';
$poll_msg_foot='';
Text that is wrapped around the list of items in the results. You use C<< >> or C<<