<?php // -*-php-*-
// rcs_id('$Id: WikiPoll.php 7638 2010-08-11 11:58:40Z vargenau $');
/*
 * Copyright 2004 $ThePhpWikiProgrammingTeam
 * Copyright 2008 Marc-Etienne Vargenau, Alcatel-lucent
 *
 * This file is part of PhpWiki.
 *
 * PhpWiki 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.
 *
 * PhpWiki 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with PhpWiki; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
/**
 * This plugin provides configurable polls.
 *
 * Usage:
<<WikiPoll require_all=0 require_least=2
           question[1]="Do you like PhpWiki?"
             answer[1][1]="Yes" answer[1][2]="Do not know" answer[1][3]="No"
           question[2]="Do you have PhpWiki installed by your own?"
             answer[2][1]="Yes" answer[2][2]="No"
           question[3]="Did you install any other wiki engine?"
             answer[3][1]="Yes" answer[3][2]="No"
           question[4]="What wiki engine do you like most?"
             answer[4][1]="c2Wiki" answer[4][2]="MoinMoin" answer[4][3]="PhpWiki"
             answer[4][4]="usemod" answer[4][5]="Twiki" answer[4][5]="guiki"
             answer[4][6]="Mediawiki" answer[4][7]="Other"
           question[5]="Which PhpWiki version do you use?"
             answer[5][1]="1.2.x" answer[5][2]="1.3.1-12" answer[5][3]="1.3.13"
             answer[5][4]="1.3.14"
>>
 *
 * Administration:
 * <<WikiPoll page=PhpWikiPoll admin=1 >>
 * and protect this page properly (e.g. PhpWikiPoll/Admin)
 *
 * TODO:
 *     admin page (view and reset statistics)
 *     for now only radio, support checkboxes (multiple selections) also?
 *
 * Author: ReiniUrban
 */

class WikiPlugin_WikiPoll
extends WikiPlugin
{
    var $_args;

    function getName () {
        return _("WikiPoll");
    }

    function getDescription () {
        return _("Enable configurable polls");
    }

    function getDefaultArguments() {
        return array('page'        => '[pagename]',
                     'admin'       => false,
                     'require_all' => 1,   // 1 if all questions must be answered
                     'require_least' => 0, // how many at least
                    );
    }

    function getArgs($argstr, $request=false, $defaults = false) {
        if ($defaults === false)
            $defaults = $this->getDefaultArguments();
        //Fixme: on POST argstr is empty
        $args = array();
        list ($argstr_args, $argstr_defaults) = $this->parseArgStr($argstr);
        if (isset($argstr_args["question_1"])) {
          $args['question'] = $this->str2array("question",$argstr_args);
          $args['answer'] = array();
          for ($i = 0; $i <= count($args['question']); $i++) {
              if ($array = $this->str2array(sprintf("%s_%d","answer",$i),$argstr_args))
                  $args['answer'][$i] = $array;
          }
        }

        if (!empty($defaults))
          foreach ($defaults as $arg => $default_val) {
            if (isset($argstr_args[$arg]))
                $args[$arg] = $argstr_args[$arg];
            elseif ( $request and ($argval = $request->getArg($arg)) !== false )
                $args[$arg] = $argval;
            elseif (isset($argstr_defaults[$arg]))
                $args[$arg] = (string) $argstr_defaults[$arg];
            else
                $args[$arg] = $default_val;

            if ($request)
                $args[$arg] = $this->expandArg($args[$arg], $request);

            unset($argstr_args[$arg]);
            unset($argstr_defaults[$arg]);
        }

        foreach (array_merge($argstr_args, $argstr_defaults) as $arg => $val) {
            if (!preg_match("/^(answer_|question_)/",$arg))
                trigger_error(sprintf(_("argument '%s' not declared by plugin"),
                                      $arg), E_USER_NOTICE);
        }

        return $args;
    }

    function handle_plugin_args_cruft($argstr, $args) {
            $argstr = str_replace("\n"," ",$argstr);
            $argstr = str_replace(array("[","]"),array("_",""),$argstr);
            $this->_args = $this->getArgs($argstr, $GLOBALS['request']);
        return;
    }

    function str2array($var, $obarray=false) {
            if (!$obarray) $obarray = $GLOBALS;
            $i = 0; $array = array();
            $name = sprintf("%s_%d",$var,$i);
            if (isset($obarray[$name])) $array[$i] = $obarray[$name];
            do {
          $i++;
          $name = sprintf("%s_%d",$var,$i);
          if (isset($obarray[$name])) $array[$i] = $obarray[$name];
            } while (isset($obarray[$name]));
            return $array;
    }

    function run($dbi, $argstr, &$request, $basepage) {
        if (!isset($_SERVER))
            $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
            $request->setArg('nocache','purge');
        $args = $this->getArgs($argstr, $request);
        if (!$args['page'])
            return $this->error("No page specified");
        if (!empty($args['admin']) and $request->_user->isAdmin()) {
            // reset statistics
            return $this->doPollAdmin($dbi, $request, $page);
        }
        extract($this->_args);
        $page = $dbi->getPage($args['page']);
        // check ip and last visit
        $poll = $page->get("poll");
        $ip = $_SERVER['REMOTE_ADDR'];
        $disable_submit = false;
        if (isset($poll['ip'][$ip]) and ((time() - $poll['ip'][$ip]) < 20*60)) {
            //view at least the result or disable the Go button
            $html = HTML(HTML::strong(
                        _("Sorry! You must wait at least 20 minutes until you can vote again!")));
            $html->pushContent($this->doPoll($page, $request, $request->getArg('answer'),true));
            return $html;
        }

        $poll['ip'][$ip] = time();
        // purge older ip's
        foreach ($poll['ip'] as $ip => $time) {
            if ((time() - $time) > 21*60)
                unset($poll['ip'][$ip]);
        }
        $html = HTML::form(array('action' => $request->getPostURL(),
                                 'method' => 'post'));

        if ($request->isPost()) {
            // checkme: check if all answers are answered
            if ($request->getArg('answer') and (
                 ($args['require_all'] and
                  count($request->getArg('answer')) == count($question))
                 or
                 ($args['require_least'] and
                  count($request->getArg('answer')) >= $args['require_least']))) {
                $page->set("poll", $poll);
                // update statistics and present them the user
                return $this->doPoll($page, $request, $request->getArg('answer'));
            } else {
                $html->pushContent(HTML::p(HTML::strong(_("Not enough questions answered!"))));
            }
        }

        $init = isset($question[0]) ? 0 : 1;
        for ($i = $init; $i <= count($question); $i++) {
            if (!isset($question[$i])) break;
            $q = $question[$i];
            if (!isset($answer[$i]))
                    trigger_error(fmt("Missing %s for %s","answer"."[$i]","question"."[$i]"),
                                  E_USER_ERROR);
            $a = $answer[$i];
            if (! is_array($a)) {
                // a simple checkbox
                $html->pushContent(HTML::p(HTML::strong($q)));
                $html->pushContent(HTML::div(
                                       HTML::input(array('type' => 'checkbox',
                                                         'name' => "answer[$i]",
                                                         'value' => 1)),
                                       HTML::raw("&nbsp;"), $a));
            } else {
                $row = HTML();
                for ($j=0; $j <= count($a); $j++) {
                    if (isset($a[$j]))
                        $row->pushContent(HTML::div(
                                              HTML::input(array('type' => 'radio',
                                                                'name' => "answer[$i]",
                                                                'value' => $j)),
                                              HTML::raw("&nbsp;"), $a[$j]));
                }
                $html->pushContent(HTML::p(HTML::strong($q)),$row);
            }
        }
        if (!$disable_submit)
            $html->pushContent(HTML::p(
                HTML::input(array('type' => 'submit',
                                  'name' => "WikiPoll",
                                  'value' => _("OK"))),
                HTML::input(array('type' => 'reset',
                                  'name' => "reset",
                                  'value' => _("Reset")))));
        else
             $html->pushContent(HTML::p(),HTML::strong(
                 _("Sorry! You must wait at least 20 minutes until you can vote again!")));
        return $html;
    }

    function bar($percent) {
        global $WikiTheme;
        return HTML(HTML::img(array('src' => $WikiTheme->getImageUrl('leftbar'),
                                    'alt' => '<')),
                    HTML::img(array('src' => $WikiTheme->getImageUrl('mainbar'),
                                    'alt' => '-',
                                    'width' => sprintf("%02d",$percent),
                                    'height' => 14)),
                    HTML::img(array('src' => $WikiTheme->getImageUrl('rightbar'),
                                    'alt' => '>')));
    }

    function doPoll($page, $request, $answers, $readonly = false) {
            $question = $this->_args['question'];
            $answer   = $this->_args['answer'];
        $html = HTML::table(array('cellspacing' => 2));
        $init = isset($question[0]) ? 0 : 1;
        for ($i = $init; $i <= count($question); $i++) {
            if (!isset($question[$i])) break;
            $poll = $page->get('poll');
            @$poll['data']['all'][$i]++;
            $q = $question[$i];
            if (!isset($answer[$i]))
                    trigger_error(fmt("Missing %s for %s","answer"."[$i]","question"."[$i]"),
                                  E_USER_ERROR);
            if (!$readonly)
                $page->set('poll',$poll);
            $a = $answer[$i];
            $result = (isset($answers[$i])) ? $answers[$i] : -1;
            if (! is_array($a) ) {
                $checkbox = HTML::input(array('type' => 'checkbox',
                                              'name' => "answer[$i]",
                                              'value' => $a));
                if ($result >= 0)
                    $checkbox->setAttr('checked', "checked");
                if (!$readonly)
                    list($percent,$count,$all) = $this->storeResult($page, $i, $result ? 1 : 0);
                else
                    list($percent,$count,$all) = $this->getResult($page, $i, 1);
                $print = sprintf(_("  %d%% (%d/%d)"), $percent, $count, $all);
                $html->pushContent(HTML::tr(HTML::th(array('colspan' => 4,'align'=>'left'),$q)));
                $html->pushContent(HTML::tr(HTML::td($checkbox),
                                            HTML::td($a),
                                                   HTML::td($this->bar($percent)),
                                            HTML::td($print)));
            } else {
                $html->pushContent(HTML::tr(HTML::th(array('colspan' => 4,'align'=>'left'),$q)));
                $row = HTML();
                if (!$readonly)
                    $this->storeResult($page, $i, $answers[$i]);
                for ($j=0; $j <= count($a); $j++) {
                    if (isset($a[$j])) {
                            list($percent,$count,$all) = $this->getResult($page,$i,$j);
                        $print = sprintf(_("  %d%% (%d/%d)"), $percent, $count, $all);
                        $radio = HTML::input(array('type' => 'radio',
                                                   'name' => "answer[$i]",
                                                   'value' => $j));
                        if ($result == $j)
                            $radio->setAttr('checked', "checked");
                        $row->pushContent(HTML::tr(HTML::td($radio),
                                                   HTML::td($a[$j]),
                                                   HTML::td($this->bar($percent)),
                                                   HTML::td($print)));
                    }
                }
                $html->pushContent($row);
            }
        }
        if (!$readonly)
            return HTML(HTML::h3(_("The result of this poll so far:")),$html,HTML::p(_("Thanks for participating!")));
        else
            return HTML(HTML::h3(_("The result of this poll so far:")),$html);

    }

    function getResult($page,$i,$j) {
            $poll = $page->get("poll");
            @$count = $poll['data']['count'][$i][$j];
            @$all = $poll['data']['all'][$i];
            $percent = sprintf("%d", $count * 100.0 / $all);
            return array($percent,$count,$all);
    }

    function storeResult($page, $i, $j) {
            $poll = $page->get("poll");
            if (!$poll) {
                $poll = array('data' => array('count' => array(),
                                                  'all'   => array()));
            }
            @$poll['data']['count'][$i][$j]++;
            //@$poll['data']['all'][$i];
            $page->set("poll",$poll);
          $percent = sprintf("%d", $poll['data']['count'][$i][$j] * 100.0 / $poll['data']['all'][$i]);
        return array($percent,$poll['data']['count'][$i][$j],$poll['data']['all'][$i]);
    }

};

// Local Variables:
// mode: php
// tab-width: 8
// c-basic-offset: 4
// c-hanging-comment-ender-p: nil
// indent-tabs-mode: nil
// End:
?>
