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