1<?php 2/** 3 * Userpoll Plugin: allows to create simple polls 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Randolf Rotta <randolf.rotta@gmail.com> 7 * 8 * heavily inspired (copy-pasted) from 9 * Esther Brunner <wikidesign@gmail.com>'s poll plugin 10 * 11 * previous version by 12 * Stephane Chazelas <stephane@artesyncp.com> 13 * 14 * latest changes: 15 * - added language support (german) 16 * - fixed the error message "Security Token did not match. Possible CSRF attack." 17 * - simplyfied source code 18 */ 19 20if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 21if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 22require_once(DOKU_PLUGIN.'syntax.php'); 23 24/** 25 * All DokuWiki plugins to extend the parser/rendering mechanism 26 * need to inherit from this class 27 */ 28class syntax_plugin_userpoll extends DokuWiki_Syntax_Plugin { 29 30 /** 31 * return some info 32 */ 33 function getInfo(){ 34 return array( 35 'author' => 'Randolf Rotta', 36 'email' => 'randolf.rotta@gmail.com', 37 'date' => '2013-05-09', 38 'name' => 'Userpoll Plugin', 39 'desc' => 'allows to create simple polls', 40 'url' => 'http://wiki.splitbrain.org/plugin:userpoll', 41 ); 42 } 43 44 function getType(){ return 'substition';} 45 function getPType(){ return 'block';} 46 function getSort(){ return 167; } 47 48 /** 49 * Connect pattern to lexer 50 */ 51 function connectTo($mode){ 52 $this->Lexer->addSpecialPattern('<userpoll.*?>.+?</userpoll>', $mode, 'plugin_userpoll'); 53 } 54 55 /** 56 * Handle the match 57 */ 58 function handle($match, $state, $pos, &$handler){ 59 $match = substr($match, 10, -11); // strip markup 60 list($title, $options) = preg_split('/>/u', $match, 2); 61 $options = explode('*', $options); 62 $text = array_shift($options); 63 64 for ($i = 0; $i < count($options); $i++){ 65 $options[$i] = trim($options[$i]); 66 } 67 return array(trim($title), trim($text), $options); 68 } 69 70 /** 71 * Create output 72 */ 73 function render($mode, &$renderer, $data) { 74 if ($mode == 'xhtml'){ 75 if (isset($_POST['vote']) && checkSecurityToken()) { 76 $this->_handlePost($data); 77 } 78 return $this->_renderPoll($renderer, $data); 79 } 80 return false; 81 } 82 83 function _handlePost($data) { 84 $options = $data[2]; 85 $title = hsc($data[0]); 86 $pollid = md5('@userpoll@'.$title); 87 $user = $_SERVER['REMOTE_USER']; 88 89 // ignore submits for other polls 90 if ($pollid != $_POST['pollid']) return; 91 92 // get poll file contents 93 $pfile = metaFN($pollid, '.userpoll'); 94 $poll = unserialize(@file_get_contents($pfile)); 95 96 if ($_POST['vote']=='@FORGET@') { 97 // user changed his/her mind, wipe him from the records 98 $opt = $poll['users'][$user]; 99 unset($poll['users'][$user]); 100 $poll['results'][$opt] -= 1; 101 $poll['votes'] -= 1; 102 } elseif (!isset($poll['users'][$user])) { 103 // user has just voted -> update results 104 $vote = $_POST['vote']; 105 $c = count($options); 106 for ($i = 0; $i < $c; $i++){ 107 $opt = hsc($options[$i]); 108 if ($vote == $opt){ 109 $poll['results'][$opt] += 1; 110 $poll['votes'] += 1; 111 $poll['users'][$user] = $opt; 112 } elseif (!isset($poll['results'][$opt])){ 113 $poll['results'][$opt] = 0; 114 } 115 } 116 } 117 118 // write poll file 119 $fh = fopen($pfile, 'w'); 120 fwrite($fh, serialize($poll)); 121 fclose($fh); 122 } 123 124 function _renderPoll(&$renderer, $data) { 125 $options = $data[2]; 126 $text = $data[1]; 127 $title = hsc($data[0]); 128 129 // prevent caching to ensure the poll results are fresh 130 $renderer->info['cache'] = false; 131 132 // get poll file contents 133 $pollid = md5('@userpoll@'.$title); 134 $pfile = metaFN($pollid, '.userpoll'); 135 $poll = unserialize(@file_get_contents($pfile)); 136 137 // output the poll 138 $renderer->doc .= '<fieldset class="userpoll">'. 139 '<legend>'.$title.'</legend>'; 140 if ($text) { 141 $renderer->doc .= '<div>'.$renderer->_xmlEntities($text).'</div>'; 142 } 143 144 if (isset($_SERVER['REMOTE_USER'])) { 145 $user = $_SERVER['REMOTE_USER']; 146 if (isset($poll['users']) && isset($poll['users'][$user])) { 147 // display results 148 $renderer->doc .= $this->_pollResults($poll, $pollid); 149 } else { 150 // display poll form 151 $renderer->doc .= $this->_pollForm($options, $pollid); 152 } 153 } else { 154 $renderer->doc .= '<div class="userpoll_error">' .$this->getLang('login_required') . '</div>' 155 . $this->_pollResults($poll); 156 } 157 $renderer->doc .= '</fieldset>'; 158 return true; 159 } 160 161 function _pollResults($poll, $pollid = false){ 162 global $ID; 163 global $lang; 164 165 $total = $poll['votes']; 166 if ($total == 0) return ''; 167 168 $ret = '<table class="blind">'; 169 $options = array_keys($poll['results']); 170 $votes = array_values($poll['results']); 171 for ($i = 0; $i < count($options); $i++){ 172 $absolute = $votes[$i]; 173 $percent = round(($absolute*100)/$total); 174 $ret .= '<tr><td class="leftalign">'.$options[$i].'</td><td><div class="userpoll_bar">'; 175 if ($percent) $ret .= '<div class="userpoll_full" style="width:'.($percent*2).'px"> </div>'; 176 $ret .= '</div></td><td class="rightalign">'.$percent.'%</td>'. 177 '<td class="rightalign">('.$absolute.')</td></tr>'; 178 } 179 if ($pollid) { 180 // user had already voted, allow him/her to changer his/her mind 181 $ret .= '<tr><td colspan="4">' 182 . '<form id="userpoll__form" method="post" action="'.script().'" accept-charset="'.$lang['encoding'].'">' 183 . '<div class="no"><input type="hidden" name="do" value="show" />' 184 . '<input type="hidden" name="pollid" value="' . $pollid . '" />' 185 . '<input type="hidden" name="id" value="'.$ID.'" />' 186 . '<input type="hidden" name="vote" value="@FORGET@">' 187 . '<input type="hidden" name="sectok" value='.getSecurityToken().' />' // einfg durch AG Server Mai 2013 188 . '<input class="button" type="submit" value="' . $this->getLang('btn_changemind') . '"></form></div></td></tr>'; 189 } 190 $ret .= '</table>'; 191 return $ret; 192 } 193 194 function _pollForm($options, $pollid){ 195 global $lang; 196 global $ID; 197 198 $ret = '<form id="userpoll__form" method="post" action="'.script().'" accept-charset="'.$lang['encoding'].'"><div class="no">'. 199 '<input type="hidden" name="do" value="show" />'. 200 '<input type="hidden" name="pollid" value="' . $pollid . '" />'. 201 '<input type="hidden" name="id" value="'.$ID.'" />'. 202 '<input type="hidden" name="sectok" value='.getSecurityToken().' />'; // einfg durch AG Server Mai 2013 203 $i = 0; 204 foreach ($options as $option){ 205 $i++; 206 $option = hsc($option); 207 $ret.= '<label class="simple" for="userpoll__option'.$i.'">'. 208 '<input type="radio" name="vote" id="userpoll__option'.$i.'" '. 209 'value="'.$option.'" /> <span>'.$option.'</span></label>'; 210 } 211 $ret .= '<input class="button" type="submit" '. 212 'value="'.$this->getLang('btn_vote').'" />'. 213 '</div></form>'; 214 return $ret; 215 } 216} 217 218//Setup VIM: ex: et ts=4 enc=utf-8 : 219