<?php
/**
 * Userpoll Plugin: allows to create simple polls
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Randolf Rotta <randolf.rotta@gmail.com>
 *
 * heavily inspired (copy-pasted) from
 *             Esther Brunner <wikidesign@gmail.com>'s poll plugin
 *
 * previous version by
 *             Stephane Chazelas <stephane@artesyncp.com>
 *
 * latest changes:
 *             - added language support (german)
 *             - fixed the error message "Security Token did not match. Possible CSRF attack."
 *             - simplyfied source code
 */
 
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' => 'Randolf Rotta',
      'email'  => 'randolf.rotta@gmail.com',
      'date'   => '2013-05-09',
      'name'   => 'Userpoll Plugin',
      'desc'   => 'allows to create simple polls',
      'url'    => 'http://wiki.splitbrain.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);
    $options = explode('*', $options);
    $text = array_shift($options);
    
    for ($i = 0; $i < count($options); $i++){
      $options[$i] = trim($options[$i]);
    }
    return array(trim($title), trim($text), $options);
  }

  /**
   * Create output
   */
  function render($mode, &$renderer, $data) {  
    if ($mode == 'xhtml'){
      if (isset($_POST['vote']) && checkSecurityToken()) {
        $this->_handlePost($data);
      }
      return $this->_renderPoll($renderer, $data);    
    }
    return false;
  }

  function _handlePost($data) {
    $options = $data[2];
    $title   = hsc($data[0]);
    $pollid = md5('@userpoll@'.$title);
    $user = $_SERVER['REMOTE_USER'];

    // ignore submits for other polls
    if ($pollid != $_POST['pollid']) return;

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

    if ($_POST['vote']=='@FORGET@') {
      // 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;
    } elseif (!isset($poll['users'][$user])) {    
      // user has just voted -> update results
      $vote = $_POST['vote'];
      $c = count($options);
      for ($i = 0; $i < $c; $i++){
        $opt = hsc($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;
        }
      }
    }
    
    // write poll file
    $fh = fopen($pfile, 'w');
    fwrite($fh, serialize($poll));
    fclose($fh);
  }

  function _renderPoll(&$renderer, $data) {
    $options = $data[2];
    $text    = $data[1]; 
    $title   = hsc($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>';
    if ($text) {
      $renderer->doc .= '<div>'.$renderer->_xmlEntities($text).'</div>';
    }

    if (isset($_SERVER['REMOTE_USER'])) {
      $user = $_SERVER['REMOTE_USER'];
      if (isset($poll['users']) && isset($poll['users'][$user])) {
        // display results
        $renderer->doc .= $this->_pollResults($poll, $pollid);
      } else {
        // display poll form
        $renderer->doc .= $this->_pollForm($options, $pollid);
      } 
    } else {
      $renderer->doc .= '<div class="userpoll_error">' .$this->getLang('login_required') . '</div>'
        . $this->_pollResults($poll);
    }
    $renderer->doc .= '</fieldset>';
    return true;
  }
  
  function _pollResults($poll, $pollid = false){
    global $ID;
    global $lang;

    $total = $poll['votes'];
    if ($total == 0) return '';
  
    $ret = '<table class="blind">';
    $options = array_keys($poll['results']);
    $votes   = array_values($poll['results']);
    for ($i = 0; $i < count($options); $i++){
      $absolute = $votes[$i];
      $percent  = round(($absolute*100)/$total);
      $ret .= '<tr><td class="leftalign">'.$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 type="hidden" name="sectok" value='.getSecurityToken().' />' // einfg durch AG Server Mai 2013
        . '<input class="button" type="submit" value="' . $this->getLang('btn_changemind') . '"></form></div></td></tr>';
    }
    $ret .= '</table>';    
    return $ret;
  }
  
  function _pollForm($options, $pollid){
    global $lang;
    global $ID;
    
    $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.'" />'.
      '<input type="hidden" name="sectok" value='.getSecurityToken().' />'; // einfg durch AG Server Mai 2013
    $i = 0;
    foreach ($options as $option){
      $i++;
      $option = hsc($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 :
