8fe81c50 |
1 | <?php |
2 | /*************************************************************************** |
3 | * Copyright (C) 2003-2007 Polytechnique.org * |
4 | * http://opensource.polytechnique.org/ * |
5 | * * |
6 | * This program is free software; you can redistribute it and/or modify * |
7 | * it under the terms of the GNU General Public License as published by * |
8 | * the Free Software Foundation; either version 2 of the License, or * |
9 | * (at your option) any later version. * |
10 | * * |
11 | * This program is distributed in the hope that it will be useful, * |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
14 | * GNU General Public License for more details. * |
15 | * * |
16 | * You should have received a copy of the GNU General Public License * |
17 | * along with this program; if not, write to the Free Software * |
18 | * Foundation, Inc., * |
19 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * |
20 | ***************************************************************************/ |
21 | |
22 | // {{{ class Survey : static database managing functions |
23 | class SurveyDB |
24 | { |
25 | // {{{ static function retrieveList() : gets the list of available survey (current, old and not validated surveys) |
26 | public static function retrieveList($type, $tpl = true) |
27 | { |
28 | switch ($type) { |
29 | case 'w': |
30 | case 'waiting' : |
31 | $where = 'valid=0'; |
32 | break; |
33 | case 'c': |
34 | case 'current': |
35 | $where = 'valid=1 AND end > NOW()'; |
36 | break; |
37 | case 'o': |
38 | case 'old': |
39 | $where = 'valid=1 AND end <= NOW()'; |
40 | break; |
41 | default: |
42 | return null; |
43 | } |
44 | $sql = 'SELECT survey_id, title, end |
45 | FROM survey_questions |
46 | WHERE '.$where.';'; |
47 | if ($tpl) { |
48 | return XDB::iterator($sql); |
49 | } else { |
50 | return XDB::iterRow($sql); |
51 | } |
52 | } |
53 | // }}} |
54 | |
55 | // {{{ static function proposeSurvey() : stores a proposition of survey in database (before validation) |
56 | public static function proposeSurvey($survey) |
57 | { |
58 | $sql = 'INSERT INTO survey_questions |
59 | SET questions={?}, |
60 | title={?}, |
61 | description={?}, |
62 | author_id={?}, |
63 | end={?}, |
64 | promos={?}, |
65 | valid=0;'; |
66 | $data = $survey->storeArray(); |
67 | return XDB::execute($sql, serialize($survey), $data['question'], $data['comment'], S::v('uid'), $data['end'], $data['promos']); |
68 | } |
69 | // }}} |
70 | |
71 | // {{{ static function updateSurvey() : updates a survey in database (before validation) |
72 | public static function updateSurvey($survey, $sid) |
73 | { |
74 | $sql = 'UPDATE survey_questions |
75 | SET questions={?}, |
76 | title={?}, |
77 | description={?}, |
78 | end={?}, |
79 | promos={?} |
80 | WHERE survey_id={?};'; |
81 | $data = $survey->storeArray(); |
82 | return XDB::execute($sql, serialize($survey), $data['question'], $data['comment'], $data['end'], $data['promos'], $sid); |
83 | } |
84 | // }}} |
85 | |
86 | // {{{ static function retrieveSurvey() : gets a survey in database (and unserialize the survey object structure) |
87 | public static function retrieveSurvey($sid) |
88 | { |
89 | $sql = 'SELECT questions, title, description, end, promos, valid |
90 | FROM survey_questions |
91 | WHERE survey_id={?}'; |
92 | $res = XDB::query($sql, $sid); |
93 | $data = $res->fetchOneAssoc(); |
94 | $survey = unserialize($data['questions']); |
95 | if (isset($data['end'])) { |
96 | $data['end'] = preg_replace('#^(\d{4})-(\d{2})-(\d{2})$#', '\3/\2/\1', $data['end']); |
97 | } |
98 | $survey->update(array('question' => $data['title'], 'comment' => $data['description'], 'end' => $data['end'], 'promos' => $data['promos'])); |
99 | $survey->setValid($data['valid']); |
100 | return $survey; |
101 | } |
102 | // }}} |
103 | |
104 | // {{{ static function retrieveSurveyInfo() : gets information about a survey (title, description, end date, restrictions) but does not unserialize the survey object structure |
105 | public static function retrieveSurveyInfo($sid) |
106 | { |
107 | $sql = 'SELECT title, description, end, promos, valid |
108 | FROM survey_questions |
109 | WHERE survey_id={?}'; |
110 | $res = XDB::query($sql, $sid); |
111 | return $res->fetchOneAssoc(); |
112 | } |
113 | // }}} |
114 | |
115 | // {{{ static function validateSurvey() : validates a survey |
116 | public static function validateSurvey($sid) |
117 | { |
118 | $sql = 'UPDATE survey_questions |
119 | SET valid=1 |
120 | WHERE survey_id={?};'; |
121 | return XDB::execute($sql, $sid); |
122 | } |
123 | // }}} |
124 | |
125 | // {{{ static function deleteSurvey() : deletes a survey (and all its votes) |
126 | public static function deleteSurvey($sid) |
127 | { |
128 | $sql1 = 'DELETE FROM survey_questions |
129 | WHERE survey_id={?};'; |
130 | $sql2 = 'DELETE FROM survey_answers |
131 | WHERE survey_id={?};'; |
132 | $sql3 = 'DELETE FROM survey_votes |
133 | WHERE survey_id={?};'; |
134 | return (XDB::execute($sql1, $sid) && XDB::execute($sql2, $sid) && XDB::execute($sql3, $sid)); |
135 | } |
136 | // }}} |
137 | } |
138 | // }}} |
139 | |
140 | // {{{ abstract class SurveyQuestion |
141 | abstract class SurveyQuestion |
142 | { |
143 | // {{{ static properties and methods regarding question types |
144 | private static $types = array('text' => 'texte court', |
145 | 'textarea' => 'texte long', |
146 | 'num' => 'numérique', |
147 | 'radio' => 'radio', |
148 | 'checkbox' => 'checkbox', |
149 | 'personal' => 'informations personnelles'); |
150 | |
151 | public static function getTypes() |
152 | { |
153 | return self::$types; |
154 | } |
155 | |
156 | public static function isType($t) |
157 | { |
158 | return array_key_exists($t, self::$types); |
159 | } |
160 | // }}} |
161 | |
162 | // {{{ common properties, constructor, and basic methods |
163 | private $survey_id; |
164 | private $id; |
165 | private $question; |
166 | private $comment; |
167 | |
8b0fa81e |
168 | protected function __construct($i, $args) |
8fe81c50 |
169 | { |
170 | $this->id = $i; |
171 | $this->update($args); |
172 | } |
173 | |
174 | protected function update($a) |
175 | { |
176 | $this->question = $a['question']; |
177 | $this->comment = $a['comment']; |
178 | } |
179 | |
180 | protected function getId() |
181 | { |
182 | return $this->id; |
183 | } |
184 | |
185 | abstract protected function getQuestionType(); |
186 | // }}} |
187 | |
188 | // {{{ tree manipulation methods : not implemented here (but definition needed) |
189 | protected function addChildNested($i, $c) |
190 | { |
191 | return false; |
192 | } |
193 | |
194 | protected function addChildAfter($i, $c) |
195 | { |
196 | return false; |
197 | } |
198 | |
199 | protected function delChild($i) |
200 | { |
201 | return false; |
202 | } |
203 | // }}} |
204 | |
205 | // {{{ function edit($i, $a) : searches and edits question $i |
206 | protected function edit($i, $a) |
207 | { |
208 | if ($this->id == $i) { |
209 | $this->update($a); |
210 | return true; |
211 | } else { |
212 | return false; |
213 | } |
214 | } |
215 | // }}} |
216 | |
217 | // {{{ functions toArray() and searchToArray($i) : (searches and) converts to array |
218 | protected function toArray() |
219 | { |
220 | return $this->storeArray(); |
221 | } |
222 | |
223 | protected function searchToArray($i) |
224 | { |
225 | if ($this->id == $i) { |
226 | return $this->storeArray(); |
227 | } else { |
228 | return null; |
229 | } |
230 | } |
231 | |
232 | protected function storeArray() |
233 | { |
234 | return array('type' => $this->getQuestionType(), 'id' => $this->id, 'question' => $this->question, 'comment' => $this->comment); |
235 | } |
236 | // }}} |
237 | |
238 | // {{{ function checkSyntax() : checks question elements (before storing into database) |
239 | protected function checkSyntax() |
240 | { |
241 | return null; |
242 | } |
243 | // }}} |
244 | |
245 | // {{{ function vote() : handles vote |
246 | protected function checkAnswer($ans) |
247 | { |
248 | return ""; |
249 | } |
250 | |
251 | function vote($sid, $vid, $a) |
252 | { |
253 | $ans = $this->checkAnswer($a[$this->getId]); |
254 | if ($ans != "") { |
255 | XDB::execute( |
256 | 'INSERT INTO survey_answers |
257 | SET survey_id = {?}, |
258 | vote_id = {?}, |
259 | question_id = {?}, |
260 | answer = "{?}"', $sid, $vid, $id, $ans); |
261 | } |
262 | } |
263 | // }}} |
264 | } |
265 | // }}} |
266 | |
267 | // {{{ abstract class SurveyTreeable extends SurveyQuestion : questions that allow nested ones |
268 | abstract class SurveyTreeable extends SurveyQuestion |
269 | { |
270 | // {{{ common properties, constructor |
271 | private $children; |
272 | |
8b0fa81e |
273 | protected function __construct($i, $args) |
8fe81c50 |
274 | { |
275 | parent::__construct($i, $args); |
276 | $this->children = array(); |
277 | } |
278 | // }}} |
279 | |
280 | // {{{ tree manipulation functions : actual implementation |
281 | protected function hasChild() |
282 | { |
283 | return !is_null($this->children) && is_array($this->children); |
284 | } |
285 | |
286 | protected function addChildNested($i, $c) |
287 | { |
288 | if ($this->getId() == $i) { |
289 | if ($this->hasChild()) { |
290 | array_unshift($this->children, $c); |
291 | } else { |
292 | $this->children = array($c); |
293 | } |
294 | return true; |
295 | } else { |
296 | foreach ($this->children as $child) { |
297 | if ($child->addChildNested($i, $c)) { |
298 | return true; |
299 | } |
300 | } |
301 | return false; |
302 | } |
303 | } |
304 | |
305 | protected function addChildAfter($i, $c) |
306 | { |
307 | $found = false; |
308 | for ($k = 0; $k < count($this->children); $k++) { |
309 | if ($this->children[$k]->getId() == $i) { |
310 | $found = true; |
311 | break; |
312 | } else { |
313 | if ($this->children[$k]->addChildAfter($i, $c)) { |
314 | return true; |
315 | } |
316 | } |
317 | } |
318 | if ($found) { |
319 | array_splice($this->children, $k+1, 0, array($c)); |
320 | return true; |
321 | } |
322 | return false; |
323 | } |
324 | |
325 | protected function delChild($i) |
326 | { |
327 | $found = false; |
328 | for ($k = 0; $k < count($this->children); $k++) { |
329 | if ($this->children[$k]->getId() == $i) { |
330 | $found = true; |
331 | break; |
332 | } else { |
333 | if ($this->children[$k]->delChild($i)) { |
334 | return true; |
335 | } |
336 | } |
337 | } |
338 | if ($found) { |
339 | array_splice($this->children, $k, 1); |
340 | return true; |
341 | } |
342 | return false; |
343 | } |
344 | // }}} |
345 | |
346 | // {{{ function edit() with tree support |
347 | protected function edit($i, $a) |
348 | { |
349 | if ($this->getId() == $i) { |
350 | $this->update($a); |
351 | return true; |
352 | } else { |
353 | foreach ($this->children as $child) { |
354 | if ($child->edit($i, $a)) { |
355 | return true; |
356 | } |
357 | } |
358 | return false; |
359 | } |
360 | } |
361 | // }}} |
362 | |
363 | // {{{ functions toArray() and searchToArray() with tree support |
364 | protected function toArray() |
365 | { |
366 | if ($this->hasChild()) { |
367 | $cArr = array(); |
368 | foreach ($this->children as $child) { |
369 | $cArr[] = $child->toArray(); |
370 | } |
371 | $a = $this->storeArray(); |
372 | $a['children'] = $cArr; |
373 | return $a; |
374 | } else { |
375 | return $this->storeArray(); |
376 | } |
377 | } |
378 | |
379 | protected function searchToArray($i) |
380 | { |
381 | if ($this->getId() == $i) { |
382 | return $this->storeArray(); |
383 | } else { |
384 | foreach ($this->children as $child) { |
385 | $a = $child->searchToArray($i); |
386 | if (!is_null($a) && is_array($a)) { |
387 | return $a; |
388 | } |
389 | } |
390 | return null; |
391 | } |
392 | } |
393 | // }}} |
394 | |
395 | // {{{ function checkSyntax() |
396 | protected function checkSyntax() |
397 | { |
398 | $rArr = array(); |
399 | foreach ($this->children as $child) { |
400 | $a = $child->checkSyntax(); |
401 | if ($a != null) { |
402 | $rArr[] = $a; |
403 | } |
404 | } |
405 | return (empty($rArr))? null : $rArr; |
406 | } |
407 | // }}} |
408 | |
409 | // {{{ function vote() |
410 | function vote($sid, $vid, $a) |
411 | { |
412 | parent::vote($sid, $vid, $a); |
413 | if ($this->hasChild()) { |
414 | foreach ($this->children as $c) { |
415 | $c->vote($sid, $vid, $a); |
416 | } |
417 | } |
418 | } |
419 | // }}} |
420 | } |
421 | // }}} |
422 | |
423 | // {{{ class SurveyRoot extends SurveyTreeable : root of any survey, actually the only entry point (no public methods outside this class) |
424 | class SurveyRoot extends SurveyTreeable |
425 | { |
426 | // {{{ properties, constructor and basic methods |
427 | private $last_id; |
428 | private $beginning; |
429 | private $end; |
430 | private $promos; |
431 | private $valid; |
432 | |
8b0fa81e |
433 | public function __construct($args) |
8fe81c50 |
434 | { |
435 | parent::__construct(0, $args); |
436 | $this->last_id = 0; |
437 | } |
438 | |
439 | public function update($args) |
440 | { |
441 | parent::update($args); |
442 | //$this->beginning = $args['beginning_year'] . "-" . $args['beginning_month'] . "-" . $args['beginning_day']; |
443 | //$this->end = $args['end_year'] . "-" . $args['end_year'] . "-" . $args['end_day']; |
444 | if (preg_match('#^\d{2}/\d{2}/\d{4}$#', $args['end'])) { |
445 | $this->end = preg_replace('#^(\d{2})/(\d{2})/(\d{4})$#', '\3-\2-\1', $args['end']); |
446 | } else { |
447 | $this->end = (preg_match('#^\d{4}-\d{2}-\d{2}$#', $args['end']))? $args['end'] : '#'; |
448 | } |
449 | $this->promos = ($args['promos'] == '' || preg_match('#^(\d{4}-?|(\d{4})?-\d{4})(,(\d{4}-?|(\d{4})?-\d{4}))*$#', $args['promos']))? $args['promos'] : '#'; |
450 | } |
451 | |
452 | private function getNextId() |
453 | { |
454 | $this->last_id++; |
455 | return $this->last_id; |
456 | } |
457 | |
458 | public function setValid($v) |
459 | { |
460 | $this->valid = (intval($v) != 0); |
461 | } |
462 | |
463 | public function isValid() |
464 | { |
465 | return $this->valid; |
466 | } |
467 | |
468 | protected function getQuestionType() |
469 | { |
470 | return "root"; |
471 | } |
472 | // }}} |
473 | |
474 | // {{{ function factory($type, $args) : builds a question according to the given type |
475 | public function factory($t, $args) |
476 | { |
477 | $i = $this->getNextId(); |
478 | switch ($t) { |
479 | case 'text': |
480 | return new SurveyText($i, $args); |
481 | case 'textarea': |
482 | return new SurveyTextarea($i, $args); |
483 | case 'num': |
484 | return new SurveyNum($i, $args); |
485 | case 'radio': |
486 | return new SurveyRadio($i, $args); |
487 | case 'checkbox': |
488 | return new SurveyCheckbox($i, $args); |
489 | case 'personal': |
490 | return new SurveyPersonal($i, $args); |
491 | default: |
492 | return null; |
493 | } |
494 | } |
495 | // }}} |
496 | |
497 | // {{{ methods needing public access |
498 | public function addChildNested($i, $c) |
499 | { |
500 | return parent::addChildNested($i, $c); |
501 | } |
502 | |
503 | public function addChildAfter($i, $c) |
504 | { |
505 | return parent::addChildAfter($i, $c); |
506 | } |
507 | |
508 | public function delChild($i) |
509 | { |
510 | return parent::delChild($i); |
511 | } |
512 | |
513 | public function edit($i, $a) |
514 | { |
515 | return parent::edit($i, $a); |
516 | } |
517 | |
518 | public function toArray() |
519 | { |
520 | return parent::toArray(); |
521 | } |
522 | |
523 | public function searchToArray($i) |
524 | { |
525 | return parent::searchToArray($i); |
526 | } |
527 | // }}} |
528 | |
529 | // {{{ function storeArray() |
530 | public function storeArray() |
531 | { |
532 | $rArr = parent::storeArray(); |
533 | $rArr['beginning'] = $this->beginning; |
534 | $rArr['end'] = $this->end; |
535 | $rArr['promos'] = $this->promos; |
536 | return $rArr; |
537 | } |
538 | // }}} |
539 | |
540 | // {{{ function checkSyntax() |
541 | private static $errorMessages = array( |
542 | "dateformat" => "la date de fin de sondage est mal formattée : elle doit respecter la syntaxe dd/mm/aaaa", |
543 | "datepassed" => "la date de fin de sondage est déjà dépassée : vous devez préciser une date future", |
544 | "promoformat" => "les restrictions à certaines promotions sont mal formattées" |
545 | ); |
546 | |
547 | public function checkSyntax() |
548 | { |
549 | $rArr = parent::checkSyntax(); |
550 | if (!preg_match('#^\d{4}-\d{2}-\d{2}$#', $this->end)) { |
551 | $rArr[] = array('question' => $this->getId(), 'error' => self::$errorMessages["dateformat"]); |
552 | } else { |
553 | if (strtotime($this->end) - time() <= 0) { |
554 | $rArr[] = array('question' => $this->getId(), 'error' => self::$errorMessages["datepassed"]); |
555 | } |
556 | } |
557 | if ($this->promos != '' && !preg_match('#^(\d{4}-?|(\d{4})?-\d{4})(,(\d{4}-?|(\d{4})?-\d{4}))*$#', $this->promos)) { |
558 | $rArr[] = array('question' => $this->getId(), 'error' => self::$errorMessages["promoformat"]); |
559 | } |
560 | return (empty($rArr))? null : $rArr; |
561 | } |
562 | // }}} |
563 | } |
564 | // }}} |
565 | |
566 | // {{{ abstract class SurveySimple extends SurveyQuestion : "opened" questions |
567 | abstract class SurveySimple extends SurveyQuestion |
568 | { |
569 | protected function checkAnswer($ans) |
570 | { |
571 | return $ans; |
572 | } |
573 | } |
574 | |
575 | // {{{ class SurveyText extends SurveySimple : simple text field, allowing a few words |
576 | class SurveyText extends SurveySimple |
577 | { |
578 | protected function getQuestionType() |
579 | { |
580 | return "text"; |
581 | } |
582 | } |
583 | // }}} |
584 | |
585 | // {{{ class SurveyTextarea extends SurveySimple : textarea field, allowing longer comments |
586 | class SurveyTextarea extends SurveySimple |
587 | { |
588 | protected function getQuestionType() |
589 | { |
590 | return "textarea"; |
591 | } |
592 | } |
593 | // }}} |
594 | |
595 | // {{{ class SurveyNum extends SurveySimple : allows numerical answers |
596 | class SurveyNum extends SurveySimple |
597 | { |
598 | protected function checkAnswer($ans) |
599 | { |
600 | return intval($ans); |
601 | } |
602 | |
603 | protected function getQuestionType() |
604 | { |
605 | return "num"; |
606 | } |
607 | } |
608 | // }}} |
609 | // }}} |
610 | |
611 | // {{{ abstract class SurveyList extends SurveyTreeable : restricted questions that allows only a list of possible answers |
612 | abstract class SurveyList extends SurveyTreeable |
613 | { |
614 | private $choices; |
615 | |
616 | protected function update($args) |
617 | { |
618 | parent::update($args); |
619 | $this->choices = explode('|', $args['options']); |
620 | } |
621 | |
622 | protected function storeArray() |
623 | { |
624 | $rArr = parent::storeArray(); |
625 | $rArr['choices'] = $this->choices; |
626 | $rArr['options'] = implode('|', $this->choices); |
627 | return $rArr; |
628 | } |
629 | |
630 | } |
631 | |
632 | // {{{ class SurveyRadio extends SurveyList : radio question, allows one answer among the list offered |
633 | class SurveyRadio extends SurveyList |
634 | { |
635 | protected function checkAnswer($ans) |
636 | { |
637 | return (in_array($ans, $this->choices)) ? $ans : ""; |
638 | } |
639 | |
640 | protected function getQuestionType() |
641 | { |
642 | return "radio"; |
643 | } |
644 | } |
645 | // }}} |
646 | |
647 | // {{{ class SurveyCheckbox extends SurveyList : checkbox question, allows any number of answers among the list offered |
648 | class SurveyCheckbox extends SurveyList |
649 | { |
650 | protected function checkAnswer($ans) |
651 | { |
652 | $rep = ""; |
653 | foreach ($this->choices as $key => $value) { |
654 | if (array_key_exists($key,$v[$id]) && $v[$id][$key]) { |
655 | $rep .= "|" . $key; |
656 | } |
657 | } |
658 | $rep = (strlen($rep) >= 4) ? substr($rep, 4) : ""; |
659 | return $rep; |
660 | } |
661 | |
662 | protected function getQuestionType() |
663 | { |
664 | return "checkbox"; |
665 | } |
666 | } |
667 | // }}} |
668 | // }}} |
669 | |
670 | // {{{ class SurveyPersonal extends SurveyQuestion : allows easy and verified access to user's personal data (promotion, name...) |
671 | class SurveyPersonal extends SurveyQuestion |
672 | { |
673 | private $perm; |
674 | |
675 | protected function update($args) |
676 | { |
677 | $args['question'] = "Informations personnelles"; |
678 | parent::update($args); |
679 | $this->perm['promo'] = isset($args['promo'])? 1 : 0; |
680 | $this->perm['name'] = isset($args['name'])? 1 : 0; |
681 | } |
682 | |
683 | protected function checkAnswer($ans) |
684 | { |
685 | if (intval($ans) == 1) { |
686 | // requete mysql qvb |
687 | return ""; |
688 | } else { |
689 | return ""; |
690 | } |
691 | } |
692 | |
693 | protected function getQuestionType() |
694 | { |
695 | return "personal"; |
696 | } |
697 | |
698 | protected function storeArray() |
699 | { |
700 | $a = parent::storeArray(); |
701 | $a['promo'] = $this->perm['promo']; |
702 | $a['name'] = $this->perm['name']; |
703 | return $a; |
704 | } |
705 | } |
706 | // }}} |
707 | |
708 | // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: |
709 | ?> |