1<?php 2/** 3* Poll Plugin: allows to create sligthly extended polls 4* 5* @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6* @author Etienne MELEARD <etienne.meleard@cru.fr> 7*/ 8 9if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 10if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 11require_once(DOKU_PLUGIN.'syntax.php'); 12 13/** 14* All DokuWiki plugins to extend the parser/rendering mechanism 15* need to inherit from this class 16*/ 17class syntax_plugin_multipoll extends DokuWiki_Syntax_Plugin { 18 19 /** 20 * return some info 21 */ 22 function getInfo() { 23 return confToHash(dirname(__FILE__).'/INFO'); 24 } 25 26 function getType() { return 'substition';} 27 function getPType() { return 'block';} 28 function getSort() { return 167; } 29 30 /** 31 * Connect pattern to lexer 32 */ 33 function connectTo($mode) { 34 $this->Lexer->addSpecialPattern('<multipoll.*?>.+?</multipoll>', $mode, 'plugin_multipoll'); 35 } 36 37 /** 38 * Handle the match 39 */ 40 function handle($match, $state, $pos, &$handler) { 41 $match = substr($match, 11, -12); // strip markup 42 if(preg_match('`^\s*((hideresults|hideifvoted|showresultsto\=(\@?[a-z0-9_-]+,)*(\@?[a-z0-9_-]+))\s+)+\|`i', $match)) { 43 $o = array_map('trim', explode(' ', substr($match, 0, strpos($match, '|')))); 44 $opts = array(); 45 foreach($o as $v) { 46 if(substr($v, 0, 14) == 'showresultsto=') { 47 $k = 'showresultsto'; 48 $v = array_map('trim', explode(',', substr($v, 14))); 49 }else{ 50 $k = $v; 51 $v = true; 52 } 53 $opts[$k] = $v; 54 } 55 $match = substr($match, strpos($match, '|') + 1); 56 } 57 $title = substr($match, 0, strpos($match, '>')); 58 $r = substr($match, strpos($match, '>') + 1); 59 $questions = array(); 60 $q = array('q' => array(), 'a' => array(), 'm' => false); 61 $lwq = true; 62 foreach(explode("\n", $r) as $l) { 63 if($lwq) { 64 if(preg_match('`^\s\s([\+\*])\s(.+)$`', $l, $m)) { 65 $lwq = false; 66 $q['a'][] = $m[2]; 67 if($m[1] == '+') $q['m'] = true; 68 }else $q['q'][] = $l; 69 }else{ 70 if(preg_match('`^\s\s([\+\*])\s(.+)$`', $l, $m)) { 71 $q['a'][] = $m[2]; 72 if($m[1] == '+') $q['m'] = true; 73 }else{ 74 $lwq = true; 75 $q['q'] = implode(' ', array_map('trim', $q['q'])); 76 $q['a'] = array_filter(array_map('trim', $q['a']), create_function('$e', 'return ($e["q"] != "");')); 77 $questions[] = $q; 78 $q = array('q' => array(), 'a' => array()); 79 $q['q'][] = $l; 80 } 81 } 82 } 83 84 if(count($questions) > 1) { // if more than 1 question => only keep questions with text 85 $questions = array_filter($questions, create_function('$e', 'return ($e["q"] != "");')); 86 } 87 88 return array(trim($title), $questions, $opts); 89 } 90 91 /** 92 * Create output 93 */ 94 function render($mode, &$renderer, $data) { 95 if ($mode == 'xhtml') { 96 global $ID; 97 global $INFO; 98 99 $opts = $data[2]; 100 $questions = $data[1]; 101 $title = $renderer->_xmlEntities($data[0]); 102 103 $authedit = (auth_quickaclcheck($ID) >= AUTH_EDIT); 104 $showresults = ((!isset($opts['hideifvoted']) && !isset($opts['hideresults'])) || $authedit); 105 $showresultsextra = false; 106 if(isset($opts['showresultsto']) && $_SERVER['REMOTE_USER']) { 107 if(in_array($_SERVER['REMOTE_USER'], $opts['showresultsto'])) $showresultsextra = true; 108 foreach($INFO['userinfo']['grps'] as $g) { 109 if(in_array('@'.$g, $opts['showresultsto'])) $showresultsextra = true; 110 } 111 } 112 $showalreadyvoted = (isset($opts['hideresults']) && !$authedit); 113 114 // prevent caching to ensure the poll results are fresh 115 $renderer->info['cache'] = false; 116 117 // get poll file contents 118 $pfile = metaFN(md5($title), '.multipoll'); 119 $poll = unserialize(@file_get_contents($pfile)); 120 121 if(!isset($poll['results'])) $poll['results'] = array(); 122 if(!isset($poll['votes'])) $poll['votes'] = 0; 123 if(!isset($poll['ips'])) $poll['ips'] = array(); 124 if(!isset($poll['users'])) $poll['users'] = array(); 125 126 $ip = clientIP(true); 127 $voted = false; 128 129 // just voted 130 if(isset($_POST['multipoll_vote'])) { 131 $save = false; 132 foreach($_POST as $k => $v) { 133 if(preg_match('`^multipoll_question_([0-9]+)$`', $k, $m)) { 134 $q = (int)$m[1]; 135 if(!isset($questions[$q])) continue; 136 if(!isset($poll['results'][$q])) $poll['results'][$q] = array(); 137 if($questions[$q]['m']) { 138 if(!is_array($v)) $v = array($v); 139 foreach($v as $ans) { 140 if(!isset($poll['results'][$q][$ans])) $poll['results'][$q][$ans] = 0; 141 $poll['results'][$q][$ans]++; 142 } 143 }else{ 144 if(is_array($v)) $v = array_shift($v); 145 if(!isset($poll['results'][$q][$v])) $poll['results'][$q][$v] = 0; 146 $poll['results'][$q][$v]++; 147 } 148 $save = true; 149 } 150 } 151 if($save) { 152 $poll['votes']++; 153 $poll['ips'][] = $ip; 154 if($_SERVER['REMOTE_USER']) $poll['users'][] = $_SERVER['REMOTE_USER']; 155 if($fh = fopen($pfile, 'w')) { 156 fwrite($fh, serialize($poll)); 157 fclose($fh); 158 } 159 $voted = true; 160 } 161 }elseif(in_array($ip, $poll['ips']) || ($_SERVER['REMOTE_USER'] && in_array($_SERVER['REMOTE_USER'], $poll['users']))) { 162 $voted = true; 163 } 164 165 if(!$voted || $showresults || $showresultsextra || $showalreadyvoted) { 166 $renderer->doc .= '<fieldset class="multipoll">'."\n".'<legend>'.$title.'</legend>'; 167 if(!$voted) $renderer->doc .= $this->_pollForm($questions, $renderer); 168 169 if($voted && $showalreadyvoted) { 170 $renderer->doc .= $this->getLang('already_voted'); 171 } 172 173 if(($voted && $showresults) || $showresultsextra) { 174 $renderer->doc .= $this->_pollResults($questions, $poll, $renderer); 175 } 176 $renderer->doc .= '</fieldset>'; 177 } 178 179 return true; 180 } 181 return false; 182 } 183 184 function _pollResults($questions, $poll, &$renderer) { 185 if($poll['votes'] == 0) return ''; 186 187 $q = 0; 188 foreach($questions as $question) { 189 $ret .= ' <div class="multipoll_question" id="poll_question_'.$q.'">'.$renderer->_xmlEntities($question['q']).'</div>'."\n"; 190 $ret .= ' <table class="blind">'."\n"; 191 foreach($question['a'] as $a) { 192 $ret .= ' <tr>'."\n"; 193 $a = $renderer->_xmlEntities($a); 194 $s = isset($poll['results'][$q]) ? (isset($poll['results'][$q][$a]) ? $poll['results'][$q][$a] : 0) : 0; 195 $pct = sprintf('%3.1f', 100 * $s / $poll['votes']); 196 $ret .= ' <td>'.$a.'</td>'."\n"; 197 $ret .= ' <td><div class="multipoll_bar">'.($s ? '<div class="multipoll_full" style="width:'.($pct * 2).'px"> </div>' : '').'</div></td>'."\n"; 198 $ret .= ' <td class="rightalign">'.$pct.'% ('.$s.')</td>'."\n"; 199 $ret .= ' </tr>'."\n"; 200 } 201 $ret .= ' </table>'."\n"; 202 $ret .= ' <br />'."\n"; 203 $q++; 204 } 205 return $ret; 206 } 207 208 function _pollForm($questions, &$renderer) { 209 global $lang; 210 global $ID; 211 212 $ret = '<form id="multipoll__form" method="post" action="'.script().'" accept-charset="'.$lang['encoding'].'">'."\n"; 213 $ret .= ' <div class="no">'."\n"; 214 $ret .= ' <input type="hidden" name="do" value="show" />'."\n"; 215 $ret .= ' <input type="hidden" name="id" value="'.$ID.'" />'."\n"; 216 $ret .= ' </div>'."\n"; 217 218 $qret = array(); 219 $q = 0; 220 foreach($questions as $question) { 221 $ret .= ' <div class="multipoll_question" id="multipoll_question_'.$q.'">'.$renderer->_xmlEntities($question['q']).'</div>'."\n"; 222 foreach($question['a'] as $a) { 223 $a = $renderer->_xmlEntities($a); 224 if($question['m']) { 225 $ret .= ' <input type="checkbox" name="multipoll_question_'.$q.'[]" value="'.$a.'" /> <span>'.$a.'</span><br />'."\n"; 226 }else $ret .= ' <input type="radio" name="multipoll_question_'.$q.'" value="'.$a.'" /> <span>'.$a.'</span><br />'."\n"; 227 } 228 $ret .= ' <hr />'."\n"; 229 $q++; 230 } 231 $ret .= ' <input class="button" type="submit" name="multipoll_vote" value="'.$this->getLang('btn_vote').'" />'."\n"; 232 $ret .= '</form>'."\n"; 233 234 return $ret; 235 } 236} 237