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