Merge branch 'xorg/1.0.2/master' into xorg/master
[platal.git] / modules / survey / survey.inc.php
1 <?php
2 /***************************************************************************
3 * Copyright (C) 2003-2010 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 require_once dirname(__FILE__) . '/question.inc.php';
23 require_once dirname(__FILE__) . '/answer.inc.php';
24
25 class Survey extends PlDBTableEntry implements SurveyQuestionContainer
26 {
27 private $fetchQuestions = true;
28 public $questions = array();
29 public $viewerFilter = null;
30 public $voterFilter = null;
31
32 public function __construct()
33 {
34 parent::__construct('surveys');
35 $this->registerFieldValidator('shortname', 'ShortNameFieldValidator');
36 $this->registerFieldFormatter('voters', 'JSonFieldFormatter');
37 $this->registerFieldFormatter('viewers', 'JSonFieldFormatter');
38 }
39
40 protected function postFetch()
41 {
42 if (!is_null($this->voters)) {
43 $this->voterFilter = UserFilter::fromExportedConditions($this->voters);
44 } else {
45 $this->voterFilter = null;
46 }
47 if (!is_null($this->viewers)) {
48 $this->viewerFilter = UserFilter::fromExportedConditions($this->viewers);
49 } else {
50 $this->viewerFilter = null;
51 }
52 if (!$this->fetchQuestions) {
53 return true;
54 }
55 $selector = new SurveyQuestion($this);
56 $selector->sid = $this->id;
57
58 $stack = array();
59 foreach ($selector as $question) {
60 $question = $question->typedInstance();
61 if (is_null($question->parent)) {
62 $this->addQuestion($question);
63 } else {
64 $pos = count($stack) - 1;
65 while ($stack[$pos]->qid != $question->parent) {
66 --$pos;
67 array_pop($stack);
68 }
69 Platal::assert(count($stack) > 0, "Invalid question structure");
70 Platal::assert($stack[$pos] instanceof SurveyQuestionContainer, "Invalid question type");
71 $stack[$pos]->addQuestion($question);
72 }
73 array_push($stack, $question);
74 }
75 return true;
76 }
77
78 protected function preSave()
79 {
80 if (!is_null($this->voterFilter)) {
81 $this->voters = $this->voterFilter->exportConditions();
82 } else {
83 $this->voters = null;
84 }
85 if (!is_null($this->viewerFilter)) {
86 $this->viewers = $this->viewerFilter->exportConditions();
87 } else {
88 $this->viewers = null;
89 }
90 return true;
91 }
92
93 protected function postSave()
94 {
95 $questions = array();
96 $selector = new SurveyQuestion($this);
97 $selector->sid = $this->id;
98 $selector->delete();
99
100 $this->reassignQuestionIds();
101 foreach ($this->questions as $question) {
102 $question->sid = $this->id;
103 $question->insert();
104 }
105 }
106
107 public function addQuestion(SurveyQuestion $question, $pos = null)
108 {
109 $question->parent = null;
110 if (is_null($pos)) {
111 $this->questions[] = $question;
112 } else {
113 array_splice($this->questions, $pos, 0, $question);
114 }
115 }
116
117 public function newQuestion($type, $pos = null)
118 {
119 $question = SurveyQuestion::instanceForType($this, $type);
120 $this->addQuestion($question, $pos);
121 return $question;
122 }
123
124 public function questionForId($qid)
125 {
126 $prev = null;
127 foreach ($this->questions as $question) {
128 if ($qid == $question->qid) {
129 return $question;
130 } else if ($qid < $question->qid) {
131 Platal::assert($prev instanceof SurveyQuestionGroup,
132 "Id gap must be caused by question groups");
133 return $prev->child($qid);
134 }
135 $prev = $question;
136 }
137 Platal::assert($prev instanceof SurveyQuestionGroup,
138 "Id gap must be caused by question groups");
139 return $prev->child($qid);
140 }
141
142 public function reassignQuestionIds()
143 {
144 $id = 0;
145 foreach ($this->questions as $question) {
146 $question->qid = $id;
147 if ($question instanceof SurveyQuestionContainer) {
148 $id = $question->reassignQuestionIds();
149 } else {
150 $id++;
151 }
152 }
153 return $id;
154 }
155
156 public function export()
157 {
158 $export = parent::export();
159 $export['questions'] = array();
160 foreach ($this->questions as $question) {
161 $export['questions'][] = $question->export();
162 }
163 return $export;
164 }
165
166 /* Return an indicator of the progression of the survey:
167 * negative values means 'the survey is not started'
168 * 0 means 'the survey is in progress'
169 * positive values means 'the survey expired'
170 */
171 public function progression()
172 {
173 if (!$this->flags->hasFlag('validated')) {
174 return -2;
175 }
176 $now = time();
177 if ($this->begin->format('U') > $now) {
178 return -1;
179 }
180 if ($this->end->format('U') <= $now) {
181 return 1;
182 }
183 return 0;
184 }
185
186 public function canSee(User $user)
187 {
188 if ($this->canSeeResults($user) || $this->canVote($user)) {
189 return true;
190 }
191 return false;
192 }
193
194 public function canSeeResults(User $user)
195 {
196 if ($user->id() == $this->uid || $user->hasFlag('admin')) {
197 return true;
198 }
199 if (is_null($this->viewerFilter)) {
200 return $this->viewerFilter->checkUser($user);
201 }
202 return false;
203 }
204
205 public function canVote(User $user)
206 {
207 $status = $this->progression();
208 if ($status < 0) {
209 return "Ce sondage n'est pas encore commencé";
210 } else if ($status > 0) {
211 return "Ce sondage est terminé";
212 }
213 if (!is_null($this->voterFilter) && !$this->voterFilter->checkUser($user)) {
214 return "Ce sondage ne s'adresse pas à toi";
215 }
216 $vote = SurveyVote::getVote($this, $user, false);
217 if (is_null($vote)) {
218 return "Tu as déjà voté à ce sondage.";
219 }
220 return true;
221 }
222
223 public function vote(User $user, array $answers)
224 {
225 if (!$this->canVote($user)) {
226 return array('survey' => "Tu n'es pas autorisé à voter à ce sondage.");
227 }
228 $vote = SurveyVote::getVote($this, $user);
229 if (is_null($vote)) {
230 return $vote;
231 }
232 $answers = new PlDict($answers);
233 foreach ($this->questions as $question) {
234 $question->vote($vote, $answers);
235 }
236 return $vote;
237 }
238
239 public static function get($name, $fetchQuestions = true)
240 {
241 if (is_array($name)) {
242 $name = $name[0];
243 }
244 $survey = new Survey();
245 $survey->fetchQuestions = $fetchQuestions;
246 if (can_convert_to_integer($name)) {
247 $survey->id = $name;
248 } else {
249 $survey->shortname = $name;
250 }
251 if (!$survey->fetch()) {
252 return null;
253 }
254 return $survey;
255 }
256
257 public static function iterActive()
258 {
259 $survey = new Survey();
260 $survey->fetchQuestions = false;
261 return $survey->iterateOnCondition('begin <= CURDATE() AND end >= CURDATE()
262 AND FIND_IN_SET(\'validated\', flags)');
263 }
264 }
265
266 class ShortNameFieldValidator implements PlDBTableFieldValidator
267 {
268 public function __construct(PlDBTableField $field, $value)
269 {
270 if (can_convert_to_integer($value) || !preg_match('/^[a-z0-9]+[-_\.a-z0-9]*[a-z0-9]+$/i', $value)) {
271 throw new PlDBBadValueException($value, $field,
272 'The shortname can only contain alphanumerical caracters, dashes, underscores and dots');
273 }
274 }
275 }
276
277 // vim:set et sw=4 sts=4 ts=4 foldmethod=marker enc=utf-8:
278 ?>