Retrieves ids from a PlSet.
[platal.git] / classes / pluser.php
index 5b36897..746ec59 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /***************************************************************************
- *  Copyright (C) 2003-2008 Polytechnique.org                              *
+ *  Copyright (C) 2003-2010 Polytechnique.org                              *
  *  http://opensource.polytechnique.org/                                   *
  *                                                                         *
  *  This program is free software; you can redistribute it and/or modify   *
@@ -32,6 +32,17 @@ class UserNotFoundException extends Exception
     }
 }
 
+interface PlUserInterface
+{
+    public static function _default_user_callback($login, $results);
+
+    /**
+     * Determines if the @p login is an email address, and an email address not
+     * served locally by plat/al.
+     */
+    public static function isForeignEmailAddress($email);
+}
+
 /**
  * Represents an user of plat/al (without any further assumption), with a
  * special focus on always-used properties (identification fields, display name,
@@ -39,26 +50,46 @@ class UserNotFoundException extends Exception
  * NOTE: each implementation of plat/al-code MUST subclass PlUser, and name it
  * 'User'.
  */
-abstract class PlUser
+abstract class PlUser implements PlUserInterface
 {
     /**
+     * User data enumerations.
+     */
+    const GENDER_FEMALE = true;
+    const GENDER_MALE = false;
+    const FORMAT_HTML = "html";
+    const FORMAT_TEXT = "text";
+
+    /**
      * User data storage.
      * By convention, null means the information hasn't been fetched yet, and
      * false means the information is not available.
      */
-    protected $user_id = null;
+
+    // uid is internal user ID (potentially numeric), whereas hruid is a
+    // "human readable" unique ID
+    protected $uid = null;
     protected $hruid = null;
 
     // User main email aliases (forlife is the for-life email address, bestalias
-    // is user-chosen preferred email address).
+    // is user-chosen preferred email address, email might be any email available
+    // for the user).
     protected $forlife = null;
     protected $bestalias = null;
+    protected $email = null;
 
     // Display name is user-chosen name to display (eg. in "Welcome
     // <display name> !"), while full name is the official full name.
     protected $display_name = null;
     protected $full_name = null;
-    protected $promo = null;
+
+    // Other important parameters used when sending emails.
+    protected $gender = null;  // Acceptable values are GENDER_MALE and GENDER_FEMALE
+    protected $email_format = null;  // Acceptable values are FORMAT_HTML and FORMAT_TEXT
+
+    // Permissions
+    protected $perms = null;
+    protected $perm_flags = null;
 
     // Other properties are listed in this key-value hash map.
     protected $data = array();
@@ -76,8 +107,8 @@ abstract class PlUser
 
         // If the user id was not part of the known values, determines it from
         // the login.
-        if (!$this->user_id) {
-            $this->user_id = $this->getLogin($login);
+        if (!$this->uid) {
+            $this->uid = $this->getLogin($login);
         }
 
         // Preloads main properties (assumes the loader will lazily get them
@@ -105,24 +136,77 @@ abstract class PlUser
      * Accessors to the main properties, ie. those available as top level
      * object variables.
      */
-    public function id() { return $this->user_id; }
-    public function login() { return $this->hruid; }
+    public function id()
+    {
+        return $this->uid;
+    }
+
+    public function login()
+    {
+        return $this->hruid;
+    }
+
+    public function isMe($other)
+    {
+        if (!$other) {
+            return false;
+        } else if ($other instanceof PlUser) {
+            return $other->id() == $this->id();
+        } else {
+            return false;
+        }
+    }
+
+    public function bestEmail()
+    {
+        if (!empty($this->bestalias)) {
+            return $this->bestalias;
+        }
+        return $this->email;
+    }
+    public function forlifeEmail()
+    {
+        if (!empty($this->forlife)) {
+            return $this->forlife;
+        }
+        return $this->email;
+    }
 
-    public function bestEmail() { return $this->bestalias; }
-    public function forlifeEmail() { return $this->forlife; }
+    public function displayName()
+    {
+        return $this->display_name;
+    }
+    public function fullName()
+    {
+        return $this->full_name;
+    }
+
+    abstract public function password();
+
+    // Fallback value is GENDER_MALE.
+    public function isFemale()
+    {
+        return $this->gender == self::GENDER_FEMALE;
+    }
 
-    public function displayName() { return $this->display_name; }
-    public function fullName() { return $this->full_name; }
+    // Fallback value is FORMAT_TEXT.
+    public function isEmailFormatHtml()
+    {
+        return $this->email_format == self::FORMAT_HTML;
+    }
 
     /**
      * Other properties are available directly through the $data array, or as
      * standard object variables, using a getter.
      */
-    public function data() { return $this->data; }
+    public function data()
+    {
+        return $this->data;
+     }
 
     public function __get($name)
     {
-        if (isset($this->$name)) {
+        if (property_exists($this, $name)) {
             return $this->$name;
         }
 
@@ -135,7 +219,16 @@ abstract class PlUser
 
     public function __isset($name)
     {
-        return isset($this->$name) || isset($this->data[$name]);
+        return property_exists($this, $name) || isset($this->data[$name]);
+    }
+
+    public function __unset($name)
+    {
+        if (property_exists($this, $name)) {
+            $this->$name = null;
+        } else {
+            unset($this->data[$name]);
+        }
     }
 
     /**
@@ -158,6 +251,41 @@ abstract class PlUser
         $this->data = array_merge($this->data, $values);
     }
 
+    /**
+     * Adds properties to the object; this method does not allow the caller to
+     * update core properties (id, ...).
+     *
+     * @param $values An associative array of non-core properties.
+     */
+    public function addProperties(array $values)
+    {
+        foreach ($values as $key => $value) {
+            if (!property_exists($this, $key)) {
+                $this->data[$key] = $value;
+            }
+        }
+    }
+
+
+    /**
+     * Build the permissions flags for the user.
+     */
+    abstract protected function buildPerms();
+
+    /**
+     * Check wether the user got the given permission combination.
+     */
+    public function checkPerms($perms)
+    {
+        if (is_null($this->perm_flags)) {
+            $this->buildPerms();
+        }
+        if (is_null($this->perm_flags)) {
+            return false;
+        }
+        return $this->perm_flags->hasFlagCombination($perms);
+    }
+
 
     /**
      * Returns a valid User object built from the @p id and optionnal @p values,
@@ -181,6 +309,11 @@ abstract class PlUser
         }
     }
 
+    public static function getWithUID($uid, $callback = false)
+    {
+        return User::getWithValues(null, array('uid' => $uid), $callback);
+    }
+
     // Same as above, but using the silent callback as default.
     public static function getSilent($login)
     {
@@ -192,25 +325,32 @@ abstract class PlUser
         return User::getWithValues($login, $values, array('User', '_silent_user_callback'));
     }
 
+    public static function getSilentWithUID($uid)
+    {
+        return User::getWithValues(null, array('uid' => $uid), array('User', '_silent_user_callback'));
+    }
+
     /**
-     * Returns forlife emails corresponding to the @p logins. If @p strict mode
-     * is disabled, it also includes login for which no forlife was found (but
-     * still call the callback for them).
+     * Retrieves User objects corresponding to the @p logins, and eventually
+     * extracts and returns the @p property. If @p strict mode is disabled, it
+     * also includes logins for which no forlife was found (but it still calls
+     * the callback for them).
      * In all cases, email addresses which are not from the local domains are
      * kept.
      *
      * @param $logins Array of user logins.
+     * @param $property Property to retrieve from the User objects.
      * @param $strict Should unvalidated logins be returned as-is or discarded ?
      * @param $callback Callback to call when a login is unknown to the system.
      * @return Array of validated user forlife emails.
      */
-    public static function getBulkForlifeEmails($logins, $strict = true, $callback = false)
+    private static function getBulkUserProperties($logins, $property, $strict, $callback)
     {
         if (!is_array($logins)) {
             if (strlen(trim($logins)) == 0) {
                 return null;
             }
-            $logins = split("[; ,\r\n\|]+", $logins);
+            $logins = preg_split("/[; ,\r\n\|]+/", $logins);
         }
 
         if ($logins) {
@@ -222,8 +362,8 @@ abstract class PlUser
                 }
 
                 if (($user = User::get($login, $callback))) {
-                    $list[$i] = $user->forlifeEmail();
-                } else if (!$strict || User::isForeignEmailAddress($login)) {
+                    $list[$i] = $user->$property();
+                } else if (!$strict || (User::isForeignEmailAddress($login) && isvalid_email($login))) {
                     $list[$i] = $login;
                 }
             }
@@ -233,6 +373,24 @@ abstract class PlUser
     }
 
     /**
+     * Returns hruid corresponding to the @p logins. See getBulkUserProperties()
+     * for details.
+     */
+    public static function getBulkHruid($logins, $callback = false)
+    {
+        return self::getBulkUserProperties($logins, 'login', true, $callback);
+    }
+
+    /**
+     * Returns forlife emails corresponding to the @p logins. See
+     * getBulkUserProperties() for details.
+     */
+    public static function getBulkForlifeEmails($logins, $strict = true, $callback = false)
+    {
+        return self::getBulkUserProperties($logins, 'forlifeEmail', $strict, $callback);
+    }
+
+    /**
      * Predefined callbacks for the user lookup; they are called when a given
      * login is found not to be associated with any valid user. Silent callback
      * does nothing; default callback is supposed to display an error message,
@@ -243,13 +401,57 @@ abstract class PlUser
         return;
     }
 
-    abstract public static function _default_user_callback($login, $results);
+    private static function stripBadChars($text)
+    {
+        return str_replace(array(' ', "'"), array('-', ''),
+                           strtolower(stripslashes(replace_accent(trim($text)))));
+    }
+
+    /** Creates a username from a first and last name
+     * @param $firstname User's firstname
+     * @param $lasttname User's lastname
+     * return STRING the corresponding username
+     */
+    public static function makeUserName($firstname, $lastname)
+    {
+        return self::stripBadChars($firstname) . '.' . self::stripBadChars($lastname);
+    }
 
     /**
-     * Determines if the @p login is an email address, and an email address not
-     * served locally by plat/al.
+     * Creates a user forlive identifier from:
+     * @param $firstname User's firstname
+     * @param $lasttname User's lastname
+     * @param $category  User's promotion or type of account
      */
-    abstract public static function isForeignEmailAddress($email);
+    public static function makeHrid($firstname, $lastname, $category)
+    {
+        $cat = self::stripBadChars($category);
+        if (!cat) {
+            Platal::page()->kill("$category is not a suitable category.");
+        }
+
+        return self::makeUserName($firstname, $lastname) . '.' . $cat;
+    }
+
+    /** Reformats the firstname so that all letters are in lower case,
+     * except the first letter of each part of the name.
+     */
+    public static function fixFirstnameCase($firstname)
+    {
+        $firstname = strtolower($firstname);
+        $pieces = explode('-', $firstname);
+
+        foreach ($pieces as $piece) {
+            $subpieces = explode("'", $piece);
+            $usubpieces = '';
+
+            foreach ($subpieces as $subpiece) {
+                $usubpieces[] = ucwords($subpiece);
+            }
+            $upieces[] = implode("'", $usubpieces);
+        }
+        return implode('-', $upieces);
+    }
 }
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: