Moving to GitHub.
[platal.git] / include / validations.inc.php
index 2d730a2..9bcde80 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /***************************************************************************
- *  Copyright (C) 2003-2009 Polytechnique.org                              *
+ *  Copyright (C) 2003-2014 Polytechnique.org                              *
  *  http://opensource.polytechnique.org/                                   *
  *                                                                         *
  *  This program is free software; you can redistribute it and/or modify   *
 define('SIZE_MAX', 32768);
 
 global $globals;
-require_once $globals->spoolroot . '/core/classes/xdb.php';
 
-/**
- * Iterator class, that lists objects through the database
- */
-class ValidateIterator extends XOrgDBIterator
-{
-    // {{{ constuctor
-
-    public function __construct ()
-    {
-        parent::__construct('SELECT data, DATE_FORMAT(stamp, "%Y%m%d%H%i%s") FROM requests ORDER BY stamp', MYSQL_NUM);
-    }
 
-    // }}}
-    // {{{ function next()
-
-    public function next ()
-    {
-        if (list($result, $stamp) = parent::next()) {
-            $result = Validate::unserialize($result);
-            $result->stamp = $stamp;
-            return($result);
-        } else {
-            return null;
-        }
-    }
-
-    // }}}
-}
-
-/** classe "virtuelle" à dériver pour chaque nouvelle implémentation
+/** Virtual class to adapt for every possible implementation.
  */
 abstract class Validate
 {
     // {{{ properties
 
     public $user;
+    public $formal;
 
     public $stamp;
     public $unique;
-    // enable the refuse button
+    // Enable the refuse button.
     public $refuse = true;
 
     public $type;
     public $comments = Array();
-    // the validations rules : comments for admins
-    public $rules = "Mieux vaut laisser une demande de validation à un autre admin que de valider une requête illégale ou que de refuser une demande légitime";
+    // Validations rules: comments for administrators.
+    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.';
+
+    // Unless differently stated, a validation must be done by a site administrator.
+    public $requireAdmin = true;
 
     // }}}
     // {{{ constructor
 
-    /** constructeur
-     * @param       $_user      user object
-     * @param       $_unique    requête pouvant être multiple ou non
-     * @param       $_type      type de la donnée comme dans le champ type de x4dat.requests
+    /** Constructor
+     * @param $_user: user object that required the validation.
+     * @param $_unique: set to false if a profile can have multiple requests of this type.
+     * @param $_type: request's type.
      */
-    public function __construct(User &$_user, $_unique, $_type)
+    public function __construct(User $_user, $_unique, $_type)
     {
         $this->user   = &$_user;
+        $this->formal = !$this->user->hasProfile();
         $this->stamp  = date('YmdHis');
         $this->unique = $_unique;
         $this->type   = $_type;
+        $this->promo  = $this->user->promo();
     }
 
     // }}}
     // {{{ function submit()
 
-    /** fonction à utiliser pour envoyer les données à la modération
-     * cette fonction supprimme les doublons sur un couple ($user,$type) si $this->unique est vrai
+    /** Sends data to validation.
+     * It also deletes multiple requests for a couple (profile, type)
+     * when $this->unique is set to true.
      */
     public function submit()
     {
         if ($this->unique) {
-            XDB::execute('DELETE FROM requests WHERE user_id={?} AND type={?}', $this->user->id(), $this->type);
+            XDB::execute('DELETE FROM  requests
+                                WHERE  uid = {?} AND type = {?}',
+                         $this->user->id(), $this->type);
         }
 
         $this->stamp = date('YmdHis');
-        XDB::execute('INSERT INTO requests (user_id, type, data, stamp) VALUES ({?}, {?}, {?}, {?})',
-                $this->user->id(), $this->type, $this, $this->stamp);
+        XDB::execute('INSERT INTO  requests (uid, type, data, stamp)
+                           VALUES  ({?}, {?}, {?}, {?})',
+                     $this->user->id(), $this->type, $this, $this->stamp);
 
         global $globals;
         $globals->updateNbValid();
@@ -113,8 +94,9 @@ abstract class Validate
 
     protected function update()
     {
-        XDB::execute('UPDATE requests SET data={?}, stamp=stamp
-                       WHERE user_id={?} AND type={?} AND stamp={?}',
+        XDB::execute('UPDATE  requests
+                         SET  data = {?}, stamp = stamp
+                       WHERE  uid = {?} AND type = {?} AND stamp = {?}',
                      $this, $this->user->id(), $this->type, $this->stamp);
         return true;
     }
@@ -122,18 +104,20 @@ abstract class Validate
     // }}}
     // {{{ function clean()
 
-    /** fonction à utiliser pour nettoyer l'entrée de la requête dans la table requests
-     * attention, tout est supprimé si c'est un unique
+    /** Deletes request from 'requests' table.
+     * If $this->unique is set, it deletes every requests of this type.
      */
     public function clean()
     {
         global $globals;
 
         if ($this->unique) {
-            $success = XDB::execute('DELETE FROM requests WHERE user_id={?} AND type={?}',
+            $success = XDB::execute('DELETE FROM  requests
+                                           WHERE  uid = {?} AND type = {?}',
                                     $this->user->id(), $this->type);
         } else {
-            $success =  XDB::execute('DELETE FROM requests WHERE user_id={?} AND type={?} AND stamp={?}',
+            $success =  XDB::execute('DELETE FROM  requests
+                                            WHERE  uid = {?} AND type = {?} AND stamp = {?}',
                                       $this->user->id(), $this->type, $this->stamp);
         }
         $globals->updateNbValid();
@@ -143,27 +127,32 @@ abstract class Validate
     // }}}
     // {{{ function handle_formu()
 
-    /** fonction à réaliser en cas de validation du formulaire
+    /** Handles form validation.
      */
     public function handle_formu()
     {
+        if ($this->requireAdmin && !S::admin()) {
+            $this->trigError('Vous n\'avez pas les permissions nécessaires pour valider cette demande.');
+            return false;
+        }
+
         if (Env::has('delete')) {
             $this->clean();
-            $this->trigSuccess('Requête supprimée');
+            $this->trigSuccess('Requête supprimée.');
             return true;
         }
 
-        // mise à jour des informations
+        // Data updates.
         if (Env::has('edit')) {
             if ($this->handle_editor()) {
                 $this->update();
-                $this->trigSuccess('Requête mise à jour');
+                $this->trigSuccess('Requête mise à jour.');
                 return true;
             }
             return false;
         }
 
-        // ajout d'un commentaire
+        // Comment addition.
         if (Env::has('hold') && Env::has('comm')) {
             $formid = Env::i('formid');
             foreach ($this->comments as $comment) {
@@ -174,9 +163,9 @@ abstract class Validate
             if (!strlen(trim(Env::v('comm')))) {
                 return true;
             }
-            $this->comments[] = Array(S::user()->login(), Env::v('comm'), $formid);
+            $this->comments[] = array(S::user()->login(), Env::v('comm'), $formid);
 
-            // envoi d'un mail à hotliners
+            // Sends email to our hotline.
             global $globals;
             $mailer = new PlMailer();
             $mailer->setSubject("Commentaires de validation {$this->type}");
@@ -192,7 +181,7 @@ abstract class Validate
             $mailer->send();
 
             $this->update();
-            $this->trigSuccess('Commentaire ajouté');
+            $this->trigSuccess('Commentaire ajouté.');
             return true;
         }
 
@@ -212,7 +201,7 @@ abstract class Validate
             if (Env::v('comm')) {
                 $this->sendmail(false);
                 $this->clean();
-                $this->trigSuccess('Email de refus envoyé');
+                $this->trigSuccess('Email de refus envoyé.');
                 return true;
             } else {
                 $this->trigError('Pas de motivation pour le refus&nbsp;!!!');
@@ -234,8 +223,13 @@ abstract class Validate
         $mailer->addTo("\"{$this->user->fullName()}\" <{$this->user->bestEmail()}>");
         $mailer->addCc("validation+{$this->type}@{$globals->mail->domain}");
 
-        $body = ($this->user->isFemale() ? "Chère camarade,\n\n" : "Cher camarade,\n\n")
-              . $this->_mail_body($isok)
+        // If the user has no profile, we should be more formal as if she has one.
+        if ($this->formal) {
+            $body = ($this->user->isFemale() ? 'Bonjour Madame' : 'Bonjour Monsieur');
+        } else {
+            $body = ($this->user->isFemale() ? 'Chère camarade' : 'Cher camarade');
+        }
+        $body .= ",\n\n" . $this->_mail_body($isok)
               . (Env::has('comm') ? "\n\n" . Env::v('comm') : '')
               . "\n\nCordialement,\n-- \nL'équipe de Polytechnique.org\n"
               . $this->_mail_ps($isok);
@@ -265,20 +259,25 @@ abstract class Validate
     // }}}
     // {{{ function get_typed_request()
 
-    /** fonction statique qui renvoie la requête de l'utilisateur d'id $uidau timestamp $t
-     * @param   $uid    l'id de l'utilisateur concerné
-     * @param   $type   le type de la requête
-     * @param   $stamp  le timestamp de la requête
+    /**
+     * @param $pid: profile's pid
+     * @param $type: request's type
+     * @param $stamp: request's timestamp
      *
-     * XXX fonction "statique" XXX
-     * à utiliser uniquement pour récupérer un objet dans la BD avec Validate::get_typed_request(...)
+     * Should only be used to retrieve an object in the databse with Validate::get_typed_request(...)
      */
     static public function get_typed_request($uid, $type, $stamp = -1)
     {
         if ($stamp == -1) {
-            $res = XDB::query('SELECT data FROM requests WHERE user_id={?} and type={?}', $uid, $type);
+            $res = XDB::query('SELECT  data
+                                 FROM  requests
+                                WHERE  uid = {?} and type = {?}',
+                              $uid, $type);
         } else {
-            $res = XDB::query('SELECT data, DATE_FORMAT(stamp, "%Y%m%d%H%i%s") FROM requests WHERE user_id={?} AND type={?} and stamp={?}', $uid, $type, $stamp);
+            $res = XDB::query('SELECT  data, DATE_FORMAT(stamp, "%Y%m%d%H%i%s")
+                                 FROM  requests
+                                WHERE  uid = {?} AND type = {?} and stamp = {?}',
+                              $uid, $type, $stamp);
         }
         if ($result = $res->fetchOneCell()) {
             $result = Validate::unserialize($result);
@@ -300,11 +299,14 @@ abstract class Validate
     // }}}
     // {{{ function get_typed_requests()
 
-    /** same as get_typed_request() but return an array of objects
+    /** Same as get_typed_request() but return an array of objects.
      */
     static public function get_typed_requests($uid, $type)
     {
-        $res = XDB::iterRow('SELECT data FROM requests WHERE user_id={?} and type={?}', $uid, $type);
+        $res = XDB::iterRow('SELECT  data
+                               FROM  requests
+                              WHERE  uid = {?} and type = {?}',
+                            $uid, $type);
         $array = array();
         while (list($data) = $res->next()) {
             $array[] = Validate::unserialize($data);
@@ -315,11 +317,14 @@ abstract class Validate
     // }}}
     // {{{ function get_typed_requests_count()
 
-    /** same as get_typed_requests() but return the count of available requests.
+    /** Same as get_typed_requests() but return the count of available requests.
      */
     static public function get_typed_requests_count($uid, $type)
     {
-        $res = XDB::query('SELECT COUNT(data) FROM requests WHERE user_id={?} and type={?}', $uid, $type);
+        $res = XDB::query('SELECT  COUNT(data)
+                             FROM  requests
+                            WHERE  uid = {?} and type = {?}',
+                          $uid, $type);
         return $res->fetchOneCell();
     }
 
@@ -344,20 +349,20 @@ abstract class Validate
     // }}}
     // {{{ function commit()
 
-    /** fonction à utiliser pour insérer les données dans x4dat
+    /** Inserts data in database.
      */
     abstract public function commit();
 
     // }}}
     // {{{ function formu()
 
-    /** nom du template qui contient le formulaire */
+    /** Retunrs the name of the form's template. */
     abstract public function formu();
 
     // }}}
     // {{{ function editor()
 
-    /** nom du formulaire d'édition */
+    /** Returns the name of the edition form's template. */
     public function editor()
     {
         return null;
@@ -366,23 +371,26 @@ abstract class Validate
     // }}}
     // {{{ function answers()
 
-    /** automatic answers table for this type of validation */
+    /** Automatic answers table for this type of validation. */
     public function answers()
     {
         static $answers_table;
         if (!isset($answers_table[$this->type])) {
-            $r = XDB::query("SELECT id, title, answer FROM requests_answers WHERE category = {?}", $this->type);
+            $r = XDB::query('SELECT  id, title, answer
+                               FROM  requests_answers
+                              WHERE  category = {?}',
+                            $this->type);
             $answers_table[$this->type] = $r->fetchAllAssoc();
         }
         return $answers_table[$this->type];
     }
 
     // }}}
-    // {{{ function id()
+    // {{{ function id()
 
     public function id()
     {
-        return $this->user->id() . '_' . $this->type . '_' . $this->stamp;
+        return str_replace(" ", "_", $this->user->id() . '_' . $this->type . '_' . $this->stamp);
     }
 
     // }}}
@@ -395,21 +403,275 @@ abstract class Validate
 
     // }}}
     // {{{ function unserialize()
+
     public static function unserialize($data)
     {
-        $obj = unserialize($data);
-        /* XXX: Temporary for hruid migration */
-        if (!isset($obj->user) || !is_object($obj)) {
-            $obj->user =& User::get($obj->forlife);
+        return unserialize($data);
+    }
+
+    // }}}
+
+    /** Return an iterator over the validation concerning the given type
+     * and the given user.
+     *
+     * @param type The type of the validations to fetch, null mean "any type"
+     * @param applyTo A User or a Profile object the validation applies to.
+     */
+    public static function iterate($type = null, $applyTo = null)
+    {
+        function toValidation($elt)
+        {
+            list($result, $stamp) = $elt;
+            $result = Validate::unserialize($result);
+            $result->stamp = $stamp;
+            return $result;
+        }
+
+        $where = array();
+        if ($type) {
+            $where[] = XDB::format('type = {?}', $type);
+        }
+        if ($applyTo) {
+            if ($applyTo instanceof User) {
+                $where[] = XDB::format('uid = {?}', $applyTo->id());
+            } else if ($applyTo instanceof Profile) {
+                $where[] = XDB::format('pid = {?}', $applyTo->id());
+            }
+        }
+        if (!empty($where)) {
+            $where = 'WHERE ' . implode('AND', $where);
+        } else {
+            $where = '';
+        }
+        $it = XDB::iterRow('SELECT  data, DATE_FORMAT(stamp, "%Y%m%d%H%i%s")
+                              FROM  requests
+                                 ' . $where . '
+                          ORDER BY  stamp');
+        return PlIteratorUtils::map($it, 'toValidation');
+    }
+}
+
+/** Virtual class for profile related validation.
+ */
+abstract class ProfileValidate extends Validate
+{
+    // {{{ properties
+
+    public $profile;
+    public $profileOwner;
+    public $userIsProfileOwner;
+    public $ownerIsRegistered;
+
+    // }}}
+    // {{{ constructor
+
+    /** Constructor
+     * @param $_user: user object that required the validation.
+     * @param $_profile: profile object that is to be modified,
+     *                   its owner (if exists) can differ from $_user.
+     * @param $_unique: set to false if a profile can have multiple requests of this type.
+     * @param $_type: request's type.
+     */
+    public function __construct(User $_user, Profile $_profile, $_unique, $_type)
+    {
+        parent::__construct($_user, $_unique, $_type);
+        $this->profile = &$_profile;
+        $this->profileOwner = $this->profile->owner();
+        $this->userIsProfileOwner = (!is_null($this->profileOwner)
+                                     && $this->profileOwner->id() == $this->user->id());
+        $this->ownerIsRegistered = $this->profile->isActive();
+    }
+
+    // }}}
+    // {{{ function submit()
+
+    /** Sends data to validation.
+     * It also deletes multiple requests for a couple (profile, type)
+     * when $this->unique is set to true.
+     */
+    public function submit()
+    {
+        if ($this->unique) {
+            XDB::execute('DELETE FROM  requests
+                                WHERE  pid = {?} AND type = {?}',
+                         $this->profile->id(), $this->type);
         }
-        /* XXX: End temporary block */
-        return $obj;
+
+        $this->stamp = date('YmdHis');
+        XDB::execute('INSERT INTO  requests (uid, pid, type, data, stamp)
+                           VALUES  ({?}, {?}, {?}, {?}, {?})',
+                     $this->user->id(), $this->profile->id(), $this->type, $this, $this->stamp);
+
+        global $globals;
+        $globals->updateNbValid();
+        return true;
     }
+
+    // }}}
+    // {{{ function update()
+
+    protected function update()
+    {
+        XDB::execute('UPDATE  requests
+                         SET  data = {?}, stamp = stamp
+                       WHERE  pid = {?} AND type = {?} AND stamp = {?}',
+                     $this, $this->profile->id(), $this->type, $this->stamp);
+        return true;
+    }
+
+    // }}}
+    // {{{ function clean()
+
+    /** Deletes request from 'requests' table.
+     * If $this->unique is set, it deletes every requests of this type.
+     */
+    public function clean()
+    {
+        global $globals;
+
+        if ($this->unique) {
+            $success = XDB::execute('DELETE FROM  requests
+                                           WHERE  pid = {?} AND type = {?}',
+                                    $this->profile->id(), $this->type);
+        } else {
+            $success =  XDB::execute('DELETE FROM  requests
+                                            WHERE  pid = {?} AND type = {?} AND stamp = {?}',
+                                      $this->profile->id(), $this->type, $this->stamp);
+        }
+        $globals->updateNbValid();
+        return $success;
+    }
+
+    // }}}
+    // {{{ function sendmail
+
+    protected function sendmail($isok)
+    {
+        // Only sends email if the profile's owner exists and is registered.
+        if ($this->ownerIsRegistered) {
+            global $globals;
+
+            $mailer = new PlMailer();
+            $mailer->setSubject($this->_mail_subj());
+            $mailer->setFrom("validation+{$this->type}@{$globals->mail->domain}");
+            $mailer->addTo("\"{$this->profile->fullName()}\" <{$this->profileOwner->bestEmail()}>");
+            $mailer->addCc("validation+{$this->type}@{$globals->mail->domain}");
+            $body = ($this->profile->isFemale() ? "Chère camarade,\n\n" : "Cher camarade,\n\n")
+                  . $this->_mail_body($isok)
+                  . (Env::has('comm') ? "\n\n" . Env::v('comm') : '')
+                  . "\n\nCordialement,\n-- \nL'équipe de Polytechnique.org\n"
+                  . $this->_mail_ps($isok);
+            $mailer->setTxtBody(wordwrap($body));
+            $mailer->send();
+        }
+    }
+
+    // }}}
+    // {{{ function get_typed_request()
+
+    /**
+     * @param $pid: profile's pid
+     * @param $type: request's type
+     * @param $stamp: request's timestamp
+     *
+     * Should only be used to retrieve an object in the databse with Validate::get_typed_request(...)
+     */
+    static public function get_typed_request($pid, $type, $stamp = -1)
+    {
+        if ($stamp == -1) {
+            $res = XDB::query('SELECT  data
+                                 FROM  requests
+                                WHERE  pid = {?} and type = {?}',
+                              $pid, $type);
+        } else {
+            $res = XDB::query('SELECT  data, DATE_FORMAT(stamp, "%Y%m%d%H%i%s")
+                                 FROM  requests
+                                WHERE  pid = {?} AND type = {?} and stamp = {?}',
+                              $pid, $type, $stamp);
+        }
+        if ($result = $res->fetchOneCell()) {
+            $result = Validate::unserialize($result);
+        } else {
+            $result = false;
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ function get_request_by_id()
+
+    static public function get_request_by_id($id)
+    {
+        list($pid, $type, $stamp) = explode('_', $id, 3);
+        return Validate::get_typed_request($pid, $type, $stamp);
+    }
+
+    // }}}
+    // {{{ function get_typed_requests()
+
+    /** Same as get_typed_request() but return an array of objects.
+     */
+    static public function get_typed_requests($pid, $type)
+    {
+        $res = XDB::iterRow('SELECT  data
+                               FROM  requests
+                              WHERE  pid = {?} and type = {?}
+                           ORDER BY  stamp',
+                            $pid, $type);
+        $array = array();
+        while (list($data) = $res->next()) {
+            $array[] = Validate::unserialize($data);
+        }
+        return $array;
+    }
+
+    // }}}
+    // {{{ function get_all_typed_requests()
+
+    /** Same as get_typed_request() but return an array of objects.
+     */
+    static public function get_all_typed_requests($type)
+    {
+        $res = XDB::iterRow('SELECT  data
+                               FROM  requests
+                              WHERE  type = {?}
+                           ORDER BY  stamp',
+                            $type);
+        $array = array();
+        while (list($data) = $res->next()) {
+            $array[] = Validate::unserialize($data);
+        }
+        return $array;
+    }
+
+    // }}}
+    // {{{ function get_typed_requests_count()
+
+    /** Same as get_typed_requests() but returns the count of available requests.
+     */
+    static public function get_typed_requests_count($pid, $type)
+    {
+        $res = XDB::query('SELECT  COUNT(data)
+                             FROM  requests
+                            WHERE  pid = {?} and type = {?}',
+                          $pid, $type);
+        return $res->fetchOneCell();
+    }
+
+    // }}}
+    // {{{ function id()
+
+    public function id()
+    {
+        return $this->profile->id() . '_' . $this->type . '_' . $this->stamp;
+    }
+
+    // }}}
 }
 
-foreach (glob(dirname(__FILE__).'/validations/*.inc.php') as $file) {
-    require_once($file);
+foreach (glob(dirname(__FILE__) . '/validations/*.inc.php') as $file) {
+    require_once $file;
 }
 
-/* vim: set expandtab shiftwidth=4 tabstop=4 softtabstop=4 foldmethod=marker enc=utf-8: */
+/* vim:set expandtab shiftwidth=4 tabstop=4 softtabstop=4 foldmethod=marker fenc=utf-8: */
 ?>