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">&nbsp;</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