]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/WikiPoll.php
renamed global $Theme to $WikiTheme (gforge nameclash)
[SourceForge/phpwiki.git] / lib / plugin / WikiPoll.php
1 <?php // -*-php-*-
2 rcs_id('$Id: WikiPoll.php,v 1.8 2004-06-14 11:31:39 rurban Exp $');
3 /*
4  Copyright 2004 $ThePhpWikiProgrammingTeam
5  
6  This file is part of PhpWiki.
7
8  PhpWiki is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12
13  PhpWiki is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with PhpWiki; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 /**
23  * This plugin provides configurable polls.
24  *
25  * Usage:
26 <?plugin WikiPoll require_all=0 require_least=2
27            question[1]="Do you like PhpWiki?"
28              answer[1][1]="Yes" answer[1][2]="Do not know" answer[1][3]="No"
29            question[2]="Do you have PhpWiki installed by your own?"
30              answer[2][1]="Yes" answer[2][2]="No"
31            question[3]="Did you install any other wiki engine?"
32              answer[3][1]="Yes" answer[3][2]="No"
33            question[4]="What wiki engine do you like most?"
34              answer[4][1]="c2Wiki" answer[4][2]="MoinMoin" answer[4][3]="PhpWiki"
35              answer[4][4]="usemod" answer[4][5]="Twiki" answer[4][5]="guiki"
36              answer[4][6]="Other"
37            question[5]="Which PhpWiki version do you use?"
38              answer[5][1]="1.2.x" answer[5][2]="1.3. 1-2" answer[5][3]="1.3.3-4"
39              answer[5][4]="1.3.5-8"
40 ?>
41  *
42  * Administration:
43  * <?plugin WikiPoll page=PhpWikiPoll admin=1 ?>
44  * and protect this page properly (e.g. PhpWikiPoll/Admin)
45  *
46  * TODO:
47  *     admin page (view and reset statistics)
48  *     for now only radio, support checkboxes (multiple selections) also?
49  *
50  * Author: ReiniUrban
51  */
52
53 class WikiPlugin_WikiPoll
54 extends WikiPlugin
55 {
56     var $_args; 
57     
58     function getName () {
59         return _("WikiPoll");
60     }
61
62     function getDescription () {
63         return _("Enable configurable polls");
64     }
65
66     function getVersion() {
67         return preg_replace("/[Revision: $]/", '',
68                             "\$Revision: 1.8 $");
69     }
70
71     function getDefaultArguments() {
72         return array('page'        => '[pagename]',
73                      'admin'       => false,
74                      'require_all' => 1,   // 1 if all questions must be answered
75                      'require_least' => 0, // how many at least
76                     );
77     }
78
79     function getArgs($argstr, $request=false, $defaults = false) {
80         if ($defaults === false)
81             $defaults = $this->getDefaultArguments();
82         //Fixme: on POST argstr is empty
83         $args = array();
84         list ($argstr_args, $argstr_defaults) = $this->parseArgStr($argstr);
85         if (isset($argstr_args["question_1"])) {
86           $args['question'] = $this->str2array("question",$argstr_args);
87           $args['answer'] = array();
88           for ($i = 0; $i <= count($args['question']); $i++) {
89               if ($array = $this->str2array(sprintf("%s_%d","answer",$i),$argstr_args))
90                   $args['answer'][$i] = $array;
91           }
92         }
93         
94         if (!empty($defaults))
95           foreach ($defaults as $arg => $default_val) {
96             if (isset($argstr_args[$arg]))
97                 $args[$arg] = $argstr_args[$arg];
98             elseif ( $request and ($argval = $request->getArg($arg)) !== false )
99                 $args[$arg] = $argval;
100             elseif (isset($argstr_defaults[$arg]))
101                 $args[$arg] = (string) $argstr_defaults[$arg];
102             else
103                 $args[$arg] = $default_val;
104
105             if ($request)
106                 $args[$arg] = $this->expandArg($args[$arg], $request);
107
108             unset($argstr_args[$arg]);
109             unset($argstr_defaults[$arg]);
110         }
111
112         foreach (array_merge($argstr_args, $argstr_defaults) as $arg => $val) {
113             if (!preg_match("/^(answer_|question_)/",$arg))
114                 trigger_error(sprintf(_("argument '%s' not declared by plugin"),
115                                       $arg), E_USER_NOTICE);
116         }
117
118         return $args;
119     }
120     
121     function handle_plugin_args_cruft($argstr, $args) {
122         $argstr = str_replace("\n"," ",$argstr);
123         $argstr = str_replace(array("[","]"),array("_",""),$argstr);
124         $this->_args = $this->getArgs($argstr, $GLOBALS['request']);
125         return;
126     }
127
128     function str2array($var, $obarray=false) {
129         if (!$obarray) $obarray = $GLOBALS;
130         $i = 0; $array = array();
131         $name = sprintf("%s_%d",$var,$i);
132         if (isset($obarray[$name])) $array[$i] = $obarray[$name];
133         do {
134           $i++;
135           $name = sprintf("%s_%d",$var,$i);
136           if (isset($obarray[$name])) $array[$i] = $obarray[$name];
137         } while (isset($obarray[$name]));
138         return $array;
139     }
140     
141     function run($dbi, $argstr, &$request, $basepage) {
142         if (!isset($_SERVER))
143             $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
144         $request->setArg('nocache','purge');
145         $args = $this->getArgs($argstr, $request);
146         if (!$args['page'])
147             return $this->error("No page specified");
148         if (!empty($args['admin']) and $request->_user->isAdmin()) {
149             // reset statistics
150             return $this->doPollAdmin($dbi, $request, $page);
151         }
152         extract($this->_args);
153         $page = $dbi->getPage($args['page']);
154         // check ip and last visit
155         $poll = $page->get("poll");
156         $ip = $_SERVER['REMOTE_ADDR'];
157         $disable_submit = false;
158         if (isset($poll['ip'][$ip]) and ((time() - $poll['ip'][$ip]) < 20*60)) {
159             //view at least the result or disable the Go button
160             $html = HTML(HTML::strong(
161                         _("Sorry! You must wait at least 20 minutes until you can vote again!")));
162             $html->pushContent($this->doPoll(&$page, &$request, $request->getArg('answer'),true));
163             return $html;
164         }
165             
166         $poll['ip'][$ip] = time();
167         // purge older ip's
168         foreach ($poll['ip'] as $ip => $time) {
169             if ((time() - $time) > 21*60)
170                 unset($poll['ip'][$ip]);
171         }
172         $html = HTML::form(array('action' => $request->getPostURL(),
173                                  'method' => 'POST'));
174
175         if ($request->isPost()) {
176             // checkme: check if all answers are answered
177             if ($request->getArg('answer') and (
178                  ($args['require_all'] and
179                   count($request->getArg('answer')) == count($question))
180                  or 
181                  ($args['require_least'] and
182                   count($request->getArg('answer')) >= $args['require_least']))) {
183                 $page->set("poll",$poll);
184                 // update statistics and present them the user
185                 return $this->doPoll(&$page, &$request, $request->getArg('answer'));
186             } else {
187                 $html->pushContent(HTML::p(HTML::strong(_("Not enough questions answered!"))));
188             }
189         }
190        
191         $init = isset($question[0]) ? 0 : 1;
192         for ($i = $init; $i <= count($question); $i++) {
193             if (!isset($question[$i])) break;
194             $q = $question[$i]; 
195             if (!isset($answer[$i]))
196                 trigger_error(fmt("Missing %s for %s","answer"."[$i]","question"."[$i]"),
197                               E_USER_ERROR);
198             $a = $answer[$i];
199             if (! is_array($a)) {
200                 // a simple checkbox
201                 $html->pushContent(HTML::p(HTML::strong($q)));
202                 $html->pushContent(HTML::div(
203                                        HTML::input(array('type' => 'checkbox',
204                                                          'name' => "answer[$i]",
205                                                          'value' => 1)),
206                                        HTML::raw("&nbsp;"), $a));
207             } else {
208                 $row = HTML();
209                 for ($j=0; $j <= count($a); $j++) {
210                     if (isset($a[$j]))
211                         $row->pushContent(HTML::div(
212                                               HTML::input(array('type' => 'radio',
213                                                                 'name' => "answer[$i]",
214                                                                 'value' => $j)),
215                                               HTML::raw("&nbsp;"), $a[$j]));
216                 }
217                 $html->pushContent(HTML::p(HTML::strong($q)),$row);
218             }
219         }
220         if (!$disable_submit)
221             $html->pushContent(HTML::p(
222                 HTML::input(array('type' => 'submit',
223                                   'name' => "WikiPoll",
224                                   'value' => _("Ok"))),
225                 HTML::input(array('type' => 'reset',
226                                   'name' => "reset",
227                                   'value' => _("Reset")))));
228         else 
229              $html->pushContent(HTML::p(),HTML::strong(
230                  _("Sorry! You must wait at least 20 minutes until you can vote again!")));
231         return $html;
232     }
233
234     function bar($percent) {
235         global $WikiTheme;
236         return HTML(HTML::img(array('src' => $WikiTheme->getImageUrl('leftbar'),
237                                     'alt' => '<')),
238                     HTML::img(array('src' => $WikiTheme->getImageUrl('mainbar'),
239                                     'alt' => '-',
240                                     'width' => sprintf("%02d",$percent),
241                                     'height' => 14)),
242                     HTML::img(array('src' => $WikiTheme->getImageUrl('rightbar'),
243                                     'alt' => '>')));
244     }
245
246     function doPoll($page, $request, $answers, $readonly = false) {
247         $question = $this->_args['question'];
248         $answer   = $this->_args['answer'];
249         $html = HTML::table(array('cellspacing' => 2));
250         $init = isset($question[0]) ? 0 : 1;
251         for ($i = $init; $i <= count($question); $i++) {
252             if (!isset($question[$i])) break;
253             $poll = $page->get('poll');
254             @$poll['data']['all'][$i]++;
255             $q = $question[$i]; 
256             if (!isset($answer[$i]))
257                 trigger_error(fmt("Missing %s for %s","answer"."[$i]","question"."[$i]"),
258                               E_USER_ERROR);
259             if (!$readonly)
260                 $page->set('poll',$poll);
261             $a = $answer[$i];
262             $result = (isset($answers[$i])) ? $answers[$i] : -1;
263             if (! is_array($a) ) {
264                 $checkbox = HTML::input(array('type' => 'checkbox',
265                                               'name' => "answer[$i]",
266                                               'value' => $a));
267                 if ($result >= 0)
268                     $checkbox->setAttr('checked',1);
269                 if (!$readonly)
270                     list($percent,$count,$all) = $this->storeResult(&$page, $i, $result ? 1 : 0);
271                 else 
272                     list($percent,$count,$all) = $this->getResult(&$page, $i, 1);
273                 $print = sprintf(_("  %d%% (%d/%d)"),$percent,$count,$all);
274                 $html->pushContent(HTML::tr(HTML::th(array('colspan' => 4,'align'=>'left'),$q)));
275                 $html->pushContent(HTML::tr(HTML::td($checkbox),
276                                             HTML::td($a),
277                                             HTML::td($this->bar($percent)),
278                                             HTML::td($print)));
279             } else {
280                 $html->pushContent(HTML::tr(HTML::th(array('colspan' => 4,'align'=>'left'),$q)));
281                 $row = HTML();
282                 if (!$readonly)
283                     $this->storeResult(&$page,$i,$answers[$i]);
284                 for ($j=0; $j <= count($a); $j++) {
285                     if (isset($a[$j])) {
286                         list($percent,$count,$all) = $this->getResult(&$page,$i,$j);
287                         $print = sprintf(_("  %d%% (%d/%d)"),$percent,$count,$all);
288                         $radio = HTML::input(array('type' => 'radio',
289                                                    'name' => "answer[$i]",
290                                                    'value' => $j));
291                         if ($result == $j)
292                             $radio->setAttr('checked',1);
293                         $row->pushContent(HTML::tr(HTML::td($radio),
294                                                    HTML::td($a[$j]),
295                                                    HTML::td($this->bar($percent)),
296                                                    HTML::td($print)));
297                     }
298                 }
299                 $html->pushContent($row);
300             }
301         }
302         if (!$readonly)
303             return HTML(HTML::h3(_("The result of this poll so far:")),$html,HTML::p(_("Thanks for participating!")));
304         else  
305             return HTML(HTML::h3(_("The result of this poll so far:")),$html);
306   
307     }
308     
309     function getResult($page,$i,$j) {
310         $poll = $page->get("poll");
311         @$count = $poll['data']['count'][$i][$j];
312         @$all = $poll['data']['all'][$i];
313         $percent = sprintf("%d", $count * 100.0 / $all);
314         return array($percent,$count,$all);
315     }
316     
317     function storeResult($page, $i, $j) {
318         $poll = $page->get("poll");
319         if (!$poll) {
320             $poll = array('data' => array('count' => array(),
321                                           'all'   => array()));
322         }
323         @$poll['data']['count'][$i][$j]++;
324         //@$poll['data']['all'][$i];
325         $page->set("poll",$poll);
326         $percent = sprintf("%d", $poll['data']['count'][$i][$j] * 100.0 / $poll['data']['all'][$i]);
327         return array($percent,$poll['data']['count'][$i][$j],$poll['data']['all'][$i]);
328     }
329
330 };
331
332 // $Log: not supported by cvs2svn $
333 // Revision 1.7  2004/05/01 15:59:29  rurban
334 // more php-4.0.6 compatibility: superglobals
335 //
336 // Revision 1.6  2004/03/01 18:08:53  rurban
337 // oops, checked in the debug version. revert to IP check on
338 //
339 // Revision 1.5  2004/03/01 16:11:13  rurban
340 // graphical enhancement
341 //
342 // Revision 1.4  2004/02/26 01:42:27  rurban
343 // don't cache this at all
344 //
345 // Revision 1.3  2004/02/24 03:54:46  rurban
346 // lock page, more questions, new require_least arg
347 //
348 // Revision 1.2  2004/02/24 03:21:46  rurban
349 // enabled require_all check in WikiPoll
350 // better handling of <20 min visiting client: display results so far
351 //
352 //
353
354 // For emacs users
355 // Local Variables:
356 // mode: php
357 // tab-width: 8
358 // c-basic-offset: 4
359 // c-hanging-comment-ender-p: nil
360 // indent-tabs-mode: nil
361 // End:
362 ?>