#
# TWiki WikiClone ($wikiversion has version info)
#
# Copyright (C) 2000-2001 Andrea Sterbini, a.sterbini@flashnet.it
# Copyright (C) 2001 Peter Thoeny, Peter@Thoeny.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details, published at
# http://www.gnu.org/copyleft/gpl.html
#
# =========================
#
# This is the default TWiki plugin. Use EmptyPlugin.pm as a template
# for your own plugins; see TWiki.TWikiPlugins for details.
#
# Each plugin is a package that contains the subs:
#
# initPlugin ( $topic, $web, $user, $installWeb )
# commonTagsHandler ( $text, $topic, $web )
# startRenderingHandler( $text, $web )
# outsidePREHandler ( $text )
# insidePREHandler ( $text )
# endRenderingHandler ( $text )
#
# initPlugin is required, all other are optional.
# For increased performance, DISABLE handlers you don't need.
# Original Poll Plugin: Collects form-fill data at the end of the page,
# and allows results to be presented as polls etc.
#
# Modified so that this is a general-purpose form-fill mechanism, that:
# 1. Collects and manages data at the end of the topic.
# 2. Allows this information to be presented as table - with configurable
# row information (i.e which columns etc.) and total information.
# 3. Types of Totals: 1. sum() 2. counts()
# 4. pre-Formfill if 'id=rowid' is given as parameter (or in the topic data.)
# The data is read from appropriate row, and form values are pre-defined.
# 5. The data can be in one topic, and be used in another topic.
#
# TablePlugin will work fine with this.
#
# TODO:
# 1. Access control for pre-FormFill data. Only user who contributed information should be able to view his own information.
# 2. Mechanism to use "ID" independent of user ID. Currently the ID of each
# row is the user ID.
# 3. Mechanism to store attribute names rather than column IDs.
# Current mechanism depends upon ordering of information received by 'poll' command.
# 4. Ability to delete a specific row.
# =========================
package TWiki::Plugins::PollPlugin;
# =========================
use vars qw( $web $topic $user $installWeb $insidePOLLVARS $votes $vWeb $vTopic $selectedRowId $selectedRow);
# =========================
sub initPlugin
{
( $topic, $web, $user, $installWeb ) = @_;
my $cgi = &TWiki::Func::getCgiQuery();
if( $cgi ) {
$selectedRowId = $cgi->param( 'id' );
}
$vWeb=$web;
$vTopic=$topic;
return 1;
}
# =========================
sub commonTagsHandler
{
### my ( $text, $topic, $web ) = @_; # do not uncomment, use $_[0], $_[1]... instead
# print "DefaultPlugin::commonTagsHandler called
";
# $_[0] =~ s/%TOPICLIST%/&handleTopicList("")/geo;
# $_[0] =~ s/%TOPICLIST{([^%}]+)}%/&handleTopicList($1)/geo;
# ??? Need to know what this means here.
my ( $tmp, $tmp1, $votes ) = split /<\!\-\-TWikiPoll\-\->/, $_[0];
$_[0] =~ s/<\!\-\-TWikiPoll\-\->.*//gso ;
$_[0] =~ s/%POLLRESULTS%/&handlePollResults("",$votes)/geo ;
$_[0] =~ s/%POLLRESULTS{(.*)}%/&handlePollResults($1,$votes)/geo ;
$_[0] =~ s/%POLLVOTERS%/&handlePollVoters("",$votes)/geo ;
$_[0] =~ s/%POLLVOTERS{(.*)}%/&handlePollVoters($1,$votes)/geo ;
# do custom extension rule, like for example:
# $_[0] =~ s/%WIKIWEB%/$TWiki::wikiToolName.$web/go;
}
# =========================
sub DISABLEstartRenderingHandler
{
### my ( $text, $web ) = @_; # do not uncomment, use $_[0], $_[1] instead
# print "DefaultPlugin::startRenderingHandler called
";
# This handler is called by getRenderedVersion just before the line loop
}
# =========================
sub outsidePREHandler
{
### my ( $text ) = @_; # do not uncomment, use $_[0] instead
# This handler is called by getRenderedVersion, in loop outside of
tag
# This is the place to define customized rendering rules
if ($insidePOLLVARS) {
if ( s/%ENDPOLLVARS%//o ) {
$insidePOLLVARS=0;
} else {
while ( s/\${id}/$selectedRowId/g ) {};
while ( s/\${(\w+)}/$selectedRow->[$1]/g ) {};
}
} elsif( s/%STARTPOLLVARS{(.*)}%//o ) {
my $attributes=$1;
my $aWeb = &TWiki::extractNameValuePair( $attributes, "web" ) || $web;
my $aTopic = &TWiki::extractNameValuePair( $attributes, "topic" ) || $topic;
my $id = &TWiki::extractNameValuePair( $attributes, "id" ) || $selectedRowId;
$selectedRowId=$id;
&populateSelectedRow($id);
# Get ID of the data. If not specified, check out cgi.
$insidePOLLVARS=1;
}
return $_[0];
}
# =========================
sub DISABLEinsidePREHandler
{
### my ( $text ) = @_; # do not uncomment, use $_[0] instead
}
# =========================
sub DISABLEendRenderingHandler
{
### my ( $text ) = @_; # do not uncomment, use $_[0] instead
# print "DefaultPlugin::endRenderingHandler called
";
# This handler is called by getRenderedVersion just after the line loop
}
# Given the ID, Populate the corresponsing row of table.
sub populateSelectedRow
{
my $id = shift;
my $aWeb = shift;
my $aTopic = shift;
my $votes = populateVotesTable($aWeb, $aTopic);
$selectedRow=$votes->{$id};
return ($selectedRow);
}
# Populate the Votes table given the web and topic name.
# $votes, $vWeb and $vTopic are globals.
sub populateVotesTable
{
my $aWeb = shift || $vWeb;
my $aTopic = shift || $vTopic;
if ( ($aWeb eq $vWeb) && ($aTopic eq $vTopic) && defined($votes) )
{
return $votes;
}
# Load the array it afresh
$votes = {};
my $text = &TWiki::Store::readWebTopic( $aWeb, $aTopic ) || "";
my ( $tmp, $tmp1, $votesStr ) = split /<\!\-\-TWikiPoll\-\->/, $text;
$votesStr =~ s/(\n|\r)+/\n/gso;
my @lines = split /\n/, $votesStr;
my ($u, $d, $i, $j, $line);
# keep only last user's vote
foreach $line ( grep { /^\s*\|.*\|\s*$/ } @lines ) {
my @items = ();
($i, $d, $u, @items) = split /\s*\|\s*/, $line;
$votes->{$u} = \@items ;
}
return $votes;
}
# =========================
sub handlePollResults {
my ( $attributes, $votes ) = @_;
my $aexequosep = &TWiki::extractNameValuePair( $attributes, "aexequosep" ) || ' ' ;
my $itemformat = &TWiki::extractNameValuePair( $attributes, "itemformat" ) || '$item' ;
my $lineformat = &TWiki::extractNameValuePair( $attributes, "lineformat" ) || '| $items | $count |' ;
my $limit = scalar &TWiki::extractNameValuePair( $attributes, "limit" ) || "5" ;
my $heading = &TWiki::extractNameValuePair( $attributes, "heading" ) ||
"| *Best $limit results* | *Votes* |" ;
my $aWeb = &TWiki::extractNameValuePair( $attributes, "web" ) || $web;
my $aTopic = &TWiki::extractNameValuePair( $attributes, "topic" ) || $topic;
# VinodKulkarni: Added rows and totals support.
my $tableHeading = &TWiki::extractNameValuePair( $attributes, "tableheading" ) || undef;
my $rowformat = &TWiki::extractNameValuePair( $attributes, "rowformat" ) || undef;
my $totalsformat = &TWiki::extractNameValuePair( $attributes, "totalsformat" ) || undef;
# Basic Work: Read and define array of results.
$votes=populateVotesTable($aWeb, $aTopic);
# Function 1: VinodKulkarni: Create and show a table in format requested.
if ($rowformat || $totalsformat ) {
my $str = $tableHeading; if ($str) { $str .= "\n" };
my $user;
# Prepare totals line: "... $counts($3) ...#
# Supports functions: $counts($column_name), $totals($column_name), ...
# For $counts, $show_only(3, $counts($column_name)) ...
if($totalsformat) {
$sums={};
$occurance={};
# Collect the names of fields for which totals are required.
$sums->{"id"}=0;
while ( $totalsformat =~ /\$sum{\s*\${(\w+)}\s*}/g ) {
$sums->{$1}=0;
}
while ( $totalsformat =~ /\$counts{\s*\${(\w+)}\s*}/g ) {
$occurance->{$1}={};
}
}
while ( ($id, $items_arr) = each %{$votes}) {
if( $rowformat ) {
$s=$rowformat; # can be null
while ( $s =~ s/\${id}/$id/g ) {};
while ( $s =~ s/\${(\w+)}/$items_arr->[$1]/g ) {};
$str .= $s . "\n";
}
$sums->{"id"}++;
if ( $totalsformat ) {
foreach $column (keys %{$sums}) {
$sums->{$column} += $items_arr->[$column];
}
}
if ( $occurance ) {
foreach $column (keys %{$occurance}) {
my $item = $items_arr->[$column];
# Increase its frequency by one.
$occurance->{$column}->{$item}++;
}
}
}
if($totalsformat) {
# Collect the names of fields for which totals are required.
$s=$totalsformat;
while ( $s =~ s/\$sum{\s*\${(\w+)}\s*}/$sums->{$1}/g ) {}
# Print occurances; for now simple, fixed format.
while ( $s =~ s/\$counts{\s*\${(\w+)}\s*}/&genCounts($1, $occurance)/geo ) {}
# How to present occurances? Use formatting again.
# $counts{${2}} - print all occurances of column 2. Choose default format.
# $count{$2, "veg"} - print value of "Veg" occurance in column 2.
# $count{$2, next} - print value of "Veg" occurance in column 2.
# $counts{${2:"$item_name\br"}} - Repeat the string for every item.
$str .= $s . "\n";
}
#return "\n$str\n ";
return $str;
}
# End of table functionality
# Function 2: VinodKulkarni: Given ID, define all field values
# NOT IMPLEMENTED YET
# now count votes
my $total = 0;
my %counts = ();
while ( ($u, $i) = each %{$voted}) {
foreach $j ( @{$i} ) {
$counts{$j}++;
$total++;
}
}
# collects all ex-aequo together
my %exequo = ();
while ( ($i, $j) = each %counts) {
my $f = $itemformat || '$item';
$f =~ s/\$item/$i/g;
if ( ! defined( $exequo{$j} ) ) {
my @a = ();
$exequo{$j} = \@a;
}
push @{$exequo{$j}}, ($f);
}
# and finally produce table
$i = 0;
my $str = "$heading\n";
foreach $j ( reverse sort keys %exequo ) {
if ( $i < $limit ) {
$i++;
#my $items = join($aexequosep, @{$exequo{$j}});
my @items = @{$exequo{$j}};
#my $lf = "$lineformat";
my $perc = $j*100/$total;
for my $item (@items) {
#$lf =~ s/\$items/$items/g;
$lf =~ s/\$items/$item/g;
$lf =~ s/\$count/$j/g;
$lf =~ s/\$perc/$perc/g;
$str .= "$lf\n";
}
}
}
return $str;
}
sub genCounts
{
my $column=shift;
my $hash2d=shift;
my $str;
my $hash=$hash2d->{$column};
foreach $instance (keys %{$hash}) {
$str .= "$instance:" . $hash->{$instance} . "
";
}
return $str;
}
# =========================
sub handlePollVoters {
my ( $attributes, $votes ) = @_;
my $lineformat = &TWiki::extractNameValuePair( $attributes, "lineformat" ) || '| $user |' ;
my $aWeb = &TWiki::extractNameValuePair( $attributes, "web" ) || $web;
my $aTopic = &TWiki::extractNameValuePair( $attributes, "topic" ) || $topic;
my $tmp = "";
my $tmp1 = "";
if (! $votes ) {
my $text = &TWiki::Store::readWebTopic( $aWeb, $aTopic ) || "";
( $tmp, $tmp1, $votes ) = split /<\!\-\-TWikiPoll\-\->/, $text;
}
$votes =~ s/(\n|\r)+/\n/gso;
my @lines = split /\n/, $votes;
my %voters = ();
my ($u, $d, $i, $j, $line);
foreach $line ( grep { /^\s*\|.*\|\s*$/ } @lines ) {
my @items = ();
($i, $d, $u, @items) = split /\s*\|\s*/, $line;
$voters{$u} = 1 ;
}
my $heading = &TWiki::extractNameValuePair( $attributes, "heading" ) ||
"| *Voters* |" ;
# and finally produce table
my $str = "$heading\n";
foreach $u ( sort keys %voters ) {
my $s = "$lineformat";
$s =~ s/\$user/$u/g;
$str .= "$s\n";
}
return $str;
}
# =========================
1;