Merge branch 'xorg/1.0.2/master' into xorg/master
[platal.git] / include / validations.inc.php
1 <?php
2 /***************************************************************************
3 * Copyright (C) 2003-2011 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 define('SIZE_MAX', 32768);
23
24 global $globals;
25
26
27 /** Virtual class to adapt for every possible implementation.
28 */
29 abstract class Validate
30 {
31 // {{{ properties
32
33 public $user;
34
35 public $stamp;
36 public $unique;
37 // Enable the refuse button.
38 public $refuse = true;
39
40 public $type;
41 public $comments = Array();
42 // Validations rules: comments for administrators.
43 public $rules = 'Mieux vaut laisser une demande de validation à un autre administrateur que de valider une requête illégale ou que de refuser une demande légitime.';
44
45 // Unless differently stated, a validation must be done by a site administrator.
46 public $requireAdmin = true;
47
48 // }}}
49 // {{{ constructor
50
51 /** Constructor
52 * @param $_user: user object that required the validation.
53 * @param $_unique: set to false if a profile can have multiple requests of this type.
54 * @param $_type: request's type.
55 */
56 public function __construct(User &$_user, $_unique, $_type)
57 {
58 $this->user = &$_user;
59 $this->stamp = date('YmdHis');
60 $this->unique = $_unique;
61 $this->type = $_type;
62 $this->promo = $this->user->promo();
63 }
64
65 // }}}
66 // {{{ function submit()
67
68 /** Sends data to validation.
69 * It also deletes multiple requests for a couple (profile, type)
70 * when $this->unique is set to true.
71 */
72 public function submit()
73 {
74 if ($this->unique) {
75 XDB::execute('DELETE FROM requests
76 WHERE uid = {?} AND type = {?}',
77 $this->user->id(), $this->type);
78 }
79
80 $this->stamp = date('YmdHis');
81 XDB::execute('INSERT INTO requests (uid, type, data, stamp)
82 VALUES ({?}, {?}, {?}, {?})',
83 $this->user->id(), $this->type, $this, $this->stamp);
84
85 global $globals;
86 $globals->updateNbValid();
87 return true;
88 }
89
90 // }}}
91 // {{{ function update()
92
93 protected function update()
94 {
95 XDB::execute('UPDATE requests
96 SET data = {?}, stamp = stamp
97 WHERE uid = {?} AND type = {?} AND stamp = {?}',
98 $this, $this->user->id(), $this->type, $this->stamp);
99 return true;
100 }
101
102 // }}}
103 // {{{ function clean()
104
105 /** Deletes request from 'requests' table.
106 * If $this->unique is set, it deletes every requests of this type.
107 */
108 public function clean()
109 {
110 global $globals;
111
112 if ($this->unique) {
113 $success = XDB::execute('DELETE FROM requests
114 WHERE uid = {?} AND type = {?}',
115 $this->user->id(), $this->type);
116 } else {
117 $success = XDB::execute('DELETE FROM requests
118 WHERE uid = {?} AND type = {?} AND stamp = {?}',
119 $this->user->id(), $this->type, $this->stamp);
120 }
121 $globals->updateNbValid();
122 return $success;
123 }
124
125 // }}}
126 // {{{ function handle_formu()
127
128 /** Handles form validation.
129 */
130 public function handle_formu()
131 {
132 if ($this->requireAdmin && !S::admin()) {
133 $this->trigError('Vous n\'avez pas les permissions nécessaires pour valider cette demande.');
134 return false;
135 }
136
137 if (Env::has('delete')) {
138 $this->clean();
139 $this->trigSuccess('Requête supprimée.');
140 return true;
141 }
142
143 // Data updates.
144 if (Env::has('edit')) {
145 if ($this->handle_editor()) {
146 $this->update();
147 $this->trigSuccess('Requête mise à jour.');
148 return true;
149 }
150 return false;
151 }
152
153 // Comment addition.
154 if (Env::has('hold') && Env::has('comm')) {
155 $formid = Env::i('formid');
156 foreach ($this->comments as $comment) {
157 if ($comment[2] === $formid) {
158 return true;
159 }
160 }
161 if (!strlen(trim(Env::v('comm')))) {
162 return true;
163 }
164 $this->comments[] = array(S::user()->login(), Env::v('comm'), $formid);
165
166 // Sends email to our hotline.
167 global $globals;
168 $mailer = new PlMailer();
169 $mailer->setSubject("Commentaires de validation {$this->type}");
170 $mailer->setFrom("validation+{$this->type}@{$globals->mail->domain}");
171 $mailer->addTo($globals->core->admin_email);
172
173 $body = "Validation {$this->type} pour {$this->user->login()}\n\n"
174 . S::user()->login() . " a ajouté le commentaire :\n\n"
175 . Env::v('comm') . "\n\n"
176 . "cf la discussion sur : " . $globals->baseurl . "/admin/validate";
177
178 $mailer->setTxtBody(wordwrap($body));
179 $mailer->send();
180
181 $this->update();
182 $this->trigSuccess('Commentaire ajouté.');
183 return true;
184 }
185
186 if (Env::has('accept')) {
187 if ($this->commit()) {
188 $this->sendmail(true);
189 $this->clean();
190 $this->trigSuccess('Email de validation envoyé');
191 return true;
192 } else {
193 $this->trigError('Erreur lors de la validation');
194 return false;
195 }
196 }
197
198 if (Env::has('refuse')) {
199 if (Env::v('comm')) {
200 $this->sendmail(false);
201 $this->clean();
202 $this->trigSuccess('Email de refus envoyé.');
203 return true;
204 } else {
205 $this->trigError('Pas de motivation pour le refus&nbsp;!!!');
206 }
207 }
208
209 return false;
210 }
211
212 // }}}
213 // {{{ function sendmail
214
215 protected function sendmail($isok)
216 {
217 global $globals;
218 $mailer = new PlMailer();
219 $mailer->setSubject($this->_mail_subj());
220 $mailer->setFrom("validation+{$this->type}@{$globals->mail->domain}");
221 $mailer->addTo("\"{$this->user->fullName()}\" <{$this->user->bestEmail()}>");
222 $mailer->addCc("validation+{$this->type}@{$globals->mail->domain}");
223
224 $body = ($this->user->isFemale() ? "Chère camarade,\n\n" : "Cher camarade,\n\n")
225 . $this->_mail_body($isok)
226 . (Env::has('comm') ? "\n\n" . Env::v('comm') : '')
227 . "\n\nCordialement,\n-- \nL'équipe de Polytechnique.org\n"
228 . $this->_mail_ps($isok);
229
230 $mailer->setTxtBody(wordwrap($body));
231 $mailer->send();
232 }
233
234 // }}}
235 // {{{ function trig()
236
237 protected function trigError($msg)
238 {
239 Platal::page()->trigError($msg);
240 }
241
242 protected function trigWarning($msg)
243 {
244 Platal::page()->trigWarning($msg);
245 }
246
247 protected function trigSuccess($msg)
248 {
249 Platal::page()->trigSuccess($msg);
250 }
251
252 // }}}
253 // {{{ function get_typed_request()
254
255 /**
256 * @param $pid: profile's pid
257 * @param $type: request's type
258 * @param $stamp: request's timestamp
259 *
260 * Should only be used to retrieve an object in the databse with Validate::get_typed_request(...)
261 */
262 static public function get_typed_request($uid, $type, $stamp = -1)
263 {
264 if ($stamp == -1) {
265 $res = XDB::query('SELECT data
266 FROM requests
267 WHERE uid = {?} and type = {?}',
268 $uid, $type);
269 } else {
270 $res = XDB::query('SELECT data, DATE_FORMAT(stamp, "%Y%m%d%H%i%s")
271 FROM requests
272 WHERE uid = {?} AND type = {?} and stamp = {?}',
273 $uid, $type, $stamp);
274 }
275 if ($result = $res->fetchOneCell()) {
276 $result = Validate::unserialize($result);
277 } else {
278 $result = false;
279 }
280 return($result);
281 }
282
283 // }}}
284 // {{{ function get_request_by_id()
285
286 static public function get_request_by_id($id)
287 {
288 list($uid, $type, $stamp) = explode('_', $id, 3);
289 return Validate::get_typed_request($uid, $type, $stamp);
290 }
291
292 // }}}
293 // {{{ function get_typed_requests()
294
295 /** Same as get_typed_request() but return an array of objects.
296 */
297 static public function get_typed_requests($uid, $type)
298 {
299 $res = XDB::iterRow('SELECT data
300 FROM requests
301 WHERE uid = {?} and type = {?}',
302 $uid, $type);
303 $array = array();
304 while (list($data) = $res->next()) {
305 $array[] = Validate::unserialize($data);
306 }
307 return $array;
308 }
309
310 // }}}
311 // {{{ function get_typed_requests_count()
312
313 /** Same as get_typed_requests() but return the count of available requests.
314 */
315 static public function get_typed_requests_count($uid, $type)
316 {
317 $res = XDB::query('SELECT COUNT(data)
318 FROM requests
319 WHERE uid = {?} and type = {?}',
320 $uid, $type);
321 return $res->fetchOneCell();
322 }
323
324 // }}}
325 // {{{ function _mail_body
326
327 abstract protected function _mail_body($isok);
328
329 // }}}
330 // {{{ function _mail_subj
331
332 abstract protected function _mail_subj();
333
334 // }}}
335 // {{{ function _mail_ps
336
337 protected function _mail_ps($isok)
338 {
339 return '';
340 }
341
342 // }}}
343 // {{{ function commit()
344
345 /** Inserts data in database.
346 */
347 abstract public function commit();
348
349 // }}}
350 // {{{ function formu()
351
352 /** Retunrs the name of the form's template. */
353 abstract public function formu();
354
355 // }}}
356 // {{{ function editor()
357
358 /** Returns the name of the edition form's template. */
359 public function editor()
360 {
361 return null;
362 }
363
364 // }}}
365 // {{{ function answers()
366
367 /** Automatic answers table for this type of validation. */
368 public function answers()
369 {
370 static $answers_table;
371 if (!isset($answers_table[$this->type])) {
372 $r = XDB::query('SELECT id, title, answer
373 FROM requests_answers
374 WHERE category = {?}',
375 $this->type);
376 $answers_table[$this->type] = $r->fetchAllAssoc();
377 }
378 return $answers_table[$this->type];
379 }
380
381 // }}}
382 // {{{ function id()
383
384 public function id()
385 {
386 return $this->user->id() . '_' . $this->type . '_' . $this->stamp;
387 }
388
389 // }}}
390 // {{{ function ruleText()
391
392 public function ruleText()
393 {
394 return str_replace('\'', '\\\'', $this->rules);
395 }
396
397 // }}}
398 // {{{ function unserialize()
399
400 public static function unserialize($data)
401 {
402 return unserialize($data);
403 }
404
405 // }}}
406
407 /** Return an iterator over the validation concerning the given type
408 * and the given user.
409 *
410 * @param type The type of the validations to fetch, null mean "any type"
411 * @param applyTo A User or a Profile object the validation applies to.
412 */
413 public static function iterate($type = null, $applyTo = null)
414 {
415 function toValidation($elt)
416 {
417 list($result, $stamp) = $elt;
418 $result = Validate::unserialize($result);
419 $result->stamp = $stamp;
420 return $result;
421 }
422
423 $where = array();
424 if ($type) {
425 $where[] = XDB::format('type = {?}', $type);
426 }
427 if ($applyTo) {
428 if ($applyTo instanceof User) {
429 $where[] = XDB::format('uid = {?}', $applyTo->id());
430 } else if ($applyTo instanceof Profile) {
431 $where[] = XDB::format('pid = {?}', $applyTo->id());
432 }
433 }
434 if (!empty($where)) {
435 $where = 'WHERE ' . implode('AND', $where);
436 }
437 $it = XDB::iterRow('SELECT data, DATE_FORMAT(stamp, "%Y%m%d%H%i%s")
438 FROM requests
439 ' . $where . '
440 ORDER BY stamp');
441 return PlIteratorUtils::map($it, 'toValidation');
442 }
443 }
444
445 /** Virtual class for profile related validation.
446 */
447 abstract class ProfileValidate extends Validate
448 {
449 // {{{ properties
450
451 public $profile;
452 public $profileOwner;
453 public $userIsProfileOwner;
454 public $ownerIsRegistered;
455
456 // }}}
457 // {{{ constructor
458
459 /** Constructor
460 * @param $_user: user object that required the validation.
461 * @param $_profile: profile object that is to be modified,
462 * its owner (if exists) can differ from $_user.
463 * @param $_unique: set to false if a profile can have multiple requests of this type.
464 * @param $_type: request's type.
465 */
466 public function __construct(User &$_user, Profile &$_profile, $_unique, $_type)
467 {
468 parent::__construct($_user, $_unique, $_type);
469 $this->profile = &$_profile;
470 $this->profileOwner = $this->profile->owner();
471 $this->userIsProfileOwner = (!is_null($this->profileOwner)
472 && $this->profileOwner->id() == $this->user->id());
473 $this->ownerIsRegistered = $this->profile->isActive();
474 }
475
476 // }}}
477 // {{{ function submit()
478
479 /** Sends data to validation.
480 * It also deletes multiple requests for a couple (profile, type)
481 * when $this->unique is set to true.
482 */
483 public function submit()
484 {
485 if ($this->unique) {
486 XDB::execute('DELETE FROM requests
487 WHERE pid = {?} AND type = {?}',
488 $this->profile->id(), $this->type);
489 }
490
491 $this->stamp = date('YmdHis');
492 XDB::execute('INSERT INTO requests (uid, pid, type, data, stamp)
493 VALUES ({?}, {?}, {?}, {?}, {?})',
494 $this->user->id(), $this->profile->id(), $this->type, $this, $this->stamp);
495
496 global $globals;
497 $globals->updateNbValid();
498 return true;
499 }
500
501 // }}}
502 // {{{ function update()
503
504 protected function update()
505 {
506 XDB::execute('UPDATE requests
507 SET data = {?}, stamp = stamp
508 WHERE pid = {?} AND type = {?} AND stamp = {?}',
509 $this, $this->profile->id(), $this->type, $this->stamp);
510 return true;
511 }
512
513 // }}}
514 // {{{ function clean()
515
516 /** Deletes request from 'requests' table.
517 * If $this->unique is set, it deletes every requests of this type.
518 */
519 public function clean()
520 {
521 global $globals;
522
523 if ($this->unique) {
524 $success = XDB::execute('DELETE FROM requests
525 WHERE pid = {?} AND type = {?}',
526 $this->profile->id(), $this->type);
527 } else {
528 $success = XDB::execute('DELETE FROM requests
529 WHERE pid = {?} AND type = {?} AND stamp = {?}',
530 $this->profile->id(), $this->type, $this->stamp);
531 }
532 $globals->updateNbValid();
533 return $success;
534 }
535
536 // }}}
537 // {{{ function sendmail
538
539 protected function sendmail($isok)
540 {
541 // Only sends email if the profile's owner exists and is registered.
542 if ($this->ownerIsRegistered) {
543 global $globals;
544
545 $mailer = new PlMailer();
546 $mailer->setSubject($this->_mail_subj());
547 $mailer->setFrom("validation+{$this->type}@{$globals->mail->domain}");
548 $mailer->addTo("\"{$this->profile->fullName()}\" <{$this->profileOwner->bestEmail()}>");
549 $mailer->addCc("validation+{$this->type}@{$globals->mail->domain}");
550 $body = ($this->profile->isFemale() ? "Chère camarade,\n\n" : "Cher camarade,\n\n")
551 . $this->_mail_body($isok)
552 . (Env::has('comm') ? "\n\n" . Env::v('comm') : '')
553 . "\n\nCordialement,\n-- \nL'équipe de Polytechnique.org\n"
554 . $this->_mail_ps($isok);
555 $mailer->setTxtBody(wordwrap($body));
556 $mailer->send();
557 }
558 }
559
560 // }}}
561 // {{{ function get_typed_request()
562
563 /**
564 * @param $pid: profile's pid
565 * @param $type: request's type
566 * @param $stamp: request's timestamp
567 *
568 * Should only be used to retrieve an object in the databse with Validate::get_typed_request(...)
569 */
570 static public function get_typed_request($pid, $type, $stamp = -1)
571 {
572 if ($stamp == -1) {
573 $res = XDB::query('SELECT data
574 FROM requests
575 WHERE pid = {?} and type = {?}',
576 $pid, $type);
577 } else {
578 $res = XDB::query('SELECT data, DATE_FORMAT(stamp, "%Y%m%d%H%i%s")
579 FROM requests
580 WHERE pid = {?} AND type = {?} and stamp = {?}',
581 $pid, $type, $stamp);
582 }
583 if ($result = $res->fetchOneCell()) {
584 $result = Validate::unserialize($result);
585 } else {
586 $result = false;
587 }
588 return $result;
589 }
590
591 // }}}
592 // {{{ function get_request_by_id()
593
594 static public function get_request_by_id($id)
595 {
596 list($pid, $type, $stamp) = explode('_', $id, 3);
597 return Validate::get_typed_request($pid, $type, $stamp);
598 }
599
600 // }}}
601 // {{{ function get_typed_requests()
602
603 /** Same as get_typed_request() but return an array of objects.
604 */
605 static public function get_typed_requests($pid, $type)
606 {
607 $res = XDB::iterRow('SELECT data
608 FROM requests
609 WHERE pid = {?} and type = {?}
610 ORDER BY stamp',
611 $pid, $type);
612 $array = array();
613 while (list($data) = $res->next()) {
614 $array[] = Validate::unserialize($data);
615 }
616 return $array;
617 }
618
619 // }}}
620 // {{{ function get_typed_requests_count()
621
622 /** Same as get_typed_requests() but returns the count of available requests.
623 */
624 static public function get_typed_requests_count($pid, $type)
625 {
626 $res = XDB::query('SELECT COUNT(data)
627 FROM requests
628 WHERE pid = {?} and type = {?}',
629 $pid, $type);
630 return $res->fetchOneCell();
631 }
632
633 // }}}
634 // {{{ function id()
635
636 public function id()
637 {
638 return $this->profile->id() . '_' . $this->type . '_' . $this->stamp;
639 }
640
641 // }}}
642 }
643
644 foreach (glob(dirname(__FILE__) . '/validations/*.inc.php') as $file) {
645 require_once $file;
646 }
647
648 /* vim: set expandtab shiftwidth=4 tabstop=4 softtabstop=4 foldmethod=marker enc=utf-8: */
649 ?>