Userpoll Plugin

Compatible with DokuWiki

2007-06-11

plugin Lets you add polls to a wiki page.

Last updated on
2007-02-09
Provides
Syntax

Similar to doodle2, multipoll, poll

Tagged with !experimental, poll

Description

Use this plugin to add a poll to a wiki page. The syntax looks like this:

<userpoll [id]>
  [question]

  * [option]
  * [option]
  * ...
</userpoll>

That means, you can simply put <userpoll> tags around regular bulleted lists to get a radio button poll.

[id] the ID of the poll; must be unique1); appears as title required
[question] the question you'd like to ask optional
[option] a possible answer to the above question required

The main difference with Esther's plugin is that this plugin is user based. That is while Esther's poll allows one vote per client IP address. This one allows one vote per logged in user. It also remembers what every voter voted, to let them change their mind if they wish.

It is compatible with the poll plugin except that if there are one poll and one userpoll on a same page, voting for userpoll will make you vote for the poll one. The contrary is not true as userpoll checks for what you're voting for, which allows you do have several userpolls in a same page.

Download & Installation

You can download and install the http://stchaz.free.fr/userpoll.zip (4.4 KB) with the plugin manager.

Source Code

<?php
/**
 * Usepoll Plugin: allows to create simple polls
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Stephane Chazelas <stephane@artesyncp.com>
 *
 * heavily inspired (copy-pasted) from
 *             Esther Brunner <wikidesign@gmail.com>'s poll plugin
 */
 
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
 
/**
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_userpoll extends DokuWiki_Syntax_Plugin {
 
  /**
   * return some info
   */
  function getInfo(){
    return array(
      'author' => 'Stephane Chazelas',
      'email'  => 'stephane@artesyncp.com',
      'date'   => '2007-02-09',
      'name'   => 'Userpoll Plugin',
      'desc'   => 'allows to create simple polls',
      'url'    => 'http://www.dokuwiki.org/plugin:userpoll',
    );
  }
 
  function getType(){ return 'substition';}
  function getPType(){ return 'block';}
  function getSort(){ return 167; }
 
  /**
   * Connect pattern to lexer
   */
  function connectTo($mode){
    $this->Lexer->addSpecialPattern('<userpoll.*?>.+?</userpoll>', $mode, 'plugin_userpoll');
  }
 
  /**
   * Handle the match
   */
  function handle($match, $state, $pos, &$handler){
    $match = substr($match, 10, -11);  // strip markup
    list($title, $options) = preg_split('/>/u', $match, 2);
    if (!$options){
      $options = $title;
      $title   = NULL;
    }
    $options = explode('*', $options);
 
    $c = count($options);
    for ($i = 0; $i < $c; $i++){
      $options[$i] = trim($options[$i]);
    }
 
    return array(trim($title), $options);
  }
 
  /**
   * Create output
   */
  function render($mode, &$renderer, $data) {
 
    if ($mode == 'xhtml'){
      global $ID;
 
      $options = $data[1];
      $title   = $renderer->_xmlEntities($data[0]);
 
      // prevent caching to ensure the poll results are fresh
      $renderer->info['cache'] = false;
 
      // get poll file contents
      $pollid = md5('@userpoll@'.$title);
      $pfile = metaFN($pollid, '.userpoll');
      $poll  = unserialize(@file_get_contents($pfile));
 
      // output the poll
      $renderer->doc .= '<fieldset class="userpoll">'.
        '<legend>'.$title.'</legend>';
      $more = trim(array_shift($options));
      if ($more){
        $renderer->doc .= '<div>'.$renderer->_xmlEntities($more).'</div>';
      }
 
      if (isset($_SERVER['REMOTE_USER'])) {
        $user = $_SERVER['REMOTE_USER'];
        if (isset($poll['users']) && isset($poll['users'][$user])) {
          if (($vote = $_REQUEST['vote']) == '@FORGET@' && $pollid == $_REQUEST['pollid']) {
            // user changed his/her mind, wipe him from the records
            $opt = $poll['users'][$user];
            unset($poll['users'][$user]);
            $poll['results'][$opt] -= 1;
            $poll['votes'] -= 1;
            $fh = fopen($pfile, 'w');
            fwrite($fh, serialize($poll));
            fclose($fh);
            $renderer->doc .= $this->_pollForm($options, $renderer, $pollid);
          } else {
            // display results
            $renderer->doc .= $this->_pollResults($poll, $pollid);
          }
 
        } elseif (($vote = $_REQUEST['vote']) && $pollid == $_REQUEST['pollid']){
 
          // user has just voted -> update results
          $c = count($options);
          for ($i = 0; $i < $c; $i++){
            $opt = $renderer->_xmlEntities($options[$i]);
            if ($vote == $opt){
              $poll['results'][$opt] += 1;
              $poll['votes'] += 1;
              $poll['users'][$user] = $opt;
            } elseif (!isset($poll['results'][$opt])){
              $poll['results'][$opt] = 0;
            }
          }
          $fh = fopen($pfile, 'w');
          fwrite($fh, serialize($poll));
          fclose($fh);
 
          // display results
          $renderer->doc .= $this->_pollResults($poll, $pollid);
        } elseif (count($options) > 0){
 
          // display poll form
          $renderer->doc .= $this->_pollForm($options, $renderer, $pollid);
 
        } else {
 
          // display results
          $renderer->doc .= $this->_pollResults($poll);
 
        }
      } else {
        $renderer->doc .= '<div class="userpoll_error">' .$this->getLang('login_required') . '</div>'
          . $this->_pollResults($poll);
      }
      $renderer->doc .= '</fieldset>';
 
      return true;
    }
    return false;
  }
 
  function _pollResults($poll, $pollid = false){
    global $ID;
    global $lang;
 
    $total = $poll['votes'];
    if ($total == 0) return '';
 
    $ret = '<table class="blind">';
    $c = count($poll['results']);
    $options = array_keys($poll['results']);
    $votes   = array_values($poll['results']);
    for ($i = 0; $i < $c; $i++){
      $absolute = $votes[$i];
      $percent  = round(($absolute*100)/$total);
      $ret .= '<tr><td>'.$options[$i].'</td><td><div class="userpoll_bar">';
      if ($percent) $ret .= '<div class="userpoll_full" style="width:'.($percent*2).'px">&nbsp;</div>';
      $ret .= '</div></td><td class="rightalign">'.$percent.'%</td>'.
        '<td class="rightalign">('.$absolute.')</td></tr>';
    }
    if ($pollid) {
      // user had already voted, allow him/her to changer his/her mind
      $ret .= '<tr><td colspan="4">'
        . '<form id="userpoll__form" method="post" action="'.script().'" accept-charset="'.$lang['encoding'].'">'
        . '<div class="no"><input type="hidden" name="do" value="show" />'
        . '<input type="hidden" name="pollid" value="' . $pollid . '" />'
        . '<input type="hidden" name="id" value="'.$ID.'" />'
        . '<input type="hidden" name="vote" value="@FORGET@">'
        . '<input class="button" type="submit" value="' . $this->getLang('btn_changemind') . '"></form></div></td></tr>';
    }
    $ret .= '</table>';
 
    return $ret;
  }
 
  function _pollForm($options, &$renderer, $pollid){
    global $lang;
    global $ID;
 
    $i = 0;
    $ret = '<form id="userpoll__form" method="post" action="'.script().'" accept-charset="'.$lang['encoding'].'"><div class="no">'.
      '<input type="hidden" name="do" value="show" />'.
      '<input type="hidden" name="pollid" value="' . $pollid . '" />'.
      '<input type="hidden" name="id" value="'.$ID.'" />';
    foreach ($options as $option){
      $i++;
      $option = $renderer->_xmlEntities($option);
      $ret.= '<label class="simple" for="userpoll__option'.$i.'">'.
        '<input type="radio" name="vote" id="userpoll__option'.$i.'" '.
        'value="'.$option.'" /> <span>'.$option.'</span></label>';
    }
    $ret .= '<input class="button" type="submit" '.
      'value="'.$this->getLang('btn_vote').'" />'.
      '</div></form>';
 
    return $ret;
  }
}
 
//Setup VIM: ex: et ts=4 enc=utf-8 :

Languages

Only English, French and Spanish supported so far.

Revision History

Discussion

Maybe you should have the title and the id different.
And maybe change the “*” that indicates a question to something else so you can do ordinary styles inside the poll.
Example:

<userpoll [ ID | Title ]>
  **We want this ?**
  <question> ====== [yes] ======</question>
  <question>[no]</question>
  <question>[Maybe]</question>
</userpoll>
Solved this idea with the following lines of code in lib/plugins/userpoll/syntax.php
If you use the syntax <userpoll [PAGEID|Title]> the ID of the current document is used as the userpoll ID or use e.g. <userpoll [123|Title]>
function render($mode, &$renderer, $data) {

  if ($mode == 'xhtml'){
    global $ID;

    $options = $data[1];
    $title   = $renderer->_xmlEntities($data[0]);

    // prevent caching to ensure the poll results are fresh
    $renderer->info['cache'] = false;

    // get poll file contents

    //starting modifications for syntax <userpoll [ID|Title]>
    $title=preg_replace( "/^\[/","",$title );
    $title=preg_replace( "/\]$/","",$title );
    if( preg_match( "/\|/", $title ) ) {
            $tmp_array = explode("|", $title);
            if( $tmp_array[0] == "PAGEID" ) {
                    $pollid = $ID;
            }
            else {
                    $pollid = $tmp_array[0];
            }
            $title = $tmp_array[1];
            if( strlen($title) > 0 ) {
                    $title = '[' . $tmp_array[1] . ']';
            }
    }
    else {
            $pollid = $title;
    }
    //$title = $title . "id=" . $pollid;  //debug

    //$pollid = md5('@userpoll@'.$title);
    $pollid = md5('@userpoll@'.$pollid);
    //end of modifications for syntax <userpoll [ID|Title]>

    $pfile = metaFN($pollid, '.userpoll');
    $poll  = unserialize(@file_get_contents($pfile));


Oli

I've excluded the chance for the users to modify their vote.. It's a very easy hack… You have simply to edit syntax.php and comment the code from line 174 to line 183.

/*if ($pollid) {
      // user had already voted, allow him/her to changer his/her mind
      $ret .= '<tr><td colspan="4">'
        . '<form id="userpoll__form" method="post" action="'.script().'" accept-charset="'.$lang['encoding'].'">'
        . '<div class="no"><input type="hidden" name="do" value="show" />'
        . '<input type="hidden" name="pollid" value="' . $pollid . '" />'
        . '<input type="hidden" name="id" value="'.$ID.'" />'
        . '<input type="hidden" name="vote" value="@FORGET@">'
        . '<input class="button" type="submit" value="' . $this->getLang('btn_changemind') . '"></form></div></td></tr>';
    }
*/

Possible Bug?

Not sure where to report bugs in this plugin. We just downloaded it and tried it in our installation (on 1/24/08). Some people were able to vote, while others were not. That is, they cast their vote, but in the results screen, they didn't see their vote reflected. I can obtain more information on this end if there's someone on that end who's interested. Thanks!

Quick update on this: I figured out what the problem was. One of the choices in the poll did not work, either because of an ampersand (&) or an apostrophe ('). I created a new poll and took those symbols out, and everything worked. Just FYI for anyone who uses this plugin.

— 04/01/2012 An error message,

Security Token did not match. Possible CSRF attack.

Something like this don't know why???

1) If it is not, metadata of the polls with the same id gets mixed up, i.e. users who have voted for a previous poll with that id can't vote again.
plugin/userpoll.txt · Last modified: 2012/01/04 13:12 by 119.82.115.146
 
Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 3.0 Unported
Imprint Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki
WikiForumIRCBugsGitXRefTranslate