2 /***************************************************************************
3 * Copyright (C) 2003-2010 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
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. *
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. *
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 *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
23 * PlUserNotFound is raised when a user id cannot be linked to a real user.
24 * The @p results give the list hruids (useful when several users are found).
26 class UserNotFoundException
extends Exception
28 public function __construct($results = array())
30 $this->results
= $results;
31 parent
::__construct();
35 interface PlUserInterface
37 public static function _default_user_callback($login, $results);
40 * Determines if the @p login is an email address, and an email address not
41 * served locally by plat/al.
43 public static function isForeignEmailAddress($email);
47 * Represents an user of plat/al (without any further assumption), with a
48 * special focus on always-used properties (identification fields, display name,
49 * forlife/bestalias emails, ...).
50 * NOTE: each implementation of plat/al-code MUST subclass PlUser, and name it
53 abstract class PlUser
implements PlUserInterface
56 * User data enumerations.
58 const GENDER_FEMALE
= true
;
59 const GENDER_MALE
= false
;
60 const FORMAT_HTML
= "html";
61 const FORMAT_TEXT
= "text";
65 * By convention, null means the information hasn't been fetched yet, and
66 * false means the information is not available.
69 // uid is internal user ID (potentially numeric), whereas hruid is a
70 // "human readable" unique ID
71 protected $uid = null
;
72 protected $hruid = null
;
74 // User main email aliases (forlife is the for-life email address, bestalias
75 // is user-chosen preferred email address, email might be any email available
77 protected $forlife = null
;
78 protected $bestalias = null
;
79 protected $email = null
;
81 // Display name is user-chosen name to display (eg. in "Welcome
82 // <display name> !"), while full name is the official full name.
83 protected $display_name = null
;
84 protected $full_name = null
;
86 // Other important parameters used when sending emails.
87 protected $gender = null
; // Acceptable values are GENDER_MALE and GENDER_FEMALE
88 protected $email_format = null
; // Acceptable values are FORMAT_HTML and FORMAT_TEXT
91 protected $perms = null
;
92 protected $perm_flags = null
;
94 // Other properties are listed in this key-value hash map.
95 protected $data = array();
98 * Constructs the PlUser object from an identifier (any identifier which is
99 * understood by getLogin() implementation).
101 * @param $login An user login.
102 * @param $values List of known user properties.
104 public function __construct($login, $values = array())
106 $this->fillFromArray($values);
108 // If the user id was not part of the known values, determines it from
111 $this->uid
= $this->getLogin($login);
114 // Preloads main properties (assumes the loader will lazily get them
115 // from variables already set in the object).
116 $this->loadMainFields();
120 * Get the canonical user id for the @p login.
122 * @param $login An user login.
123 * @return The canonical user id.
124 * @throws UserNotFoundException when login is not found.
126 abstract protected function getLogin($login);
129 * Loads the main properties (hruid, forlife, bestalias, ...) from the
130 * database. Should return immediately when the properties are already
133 abstract protected function loadMainFields();
136 * Accessors to the main properties, ie. those available as top level
144 public function login()
149 public function isMe($other)
153 } else if ($other instanceof PlUser
) {
154 return $other->id() == $this->id();
160 public function bestEmail()
162 if (!empty($this->bestalias
)) {
163 return $this->bestalias
;
167 public function forlifeEmail()
169 if (!empty($this->forlife
)) {
170 return $this->forlife
;
175 public function displayName()
177 return $this->display_name
;
179 public function fullName()
181 return $this->full_name
;
184 abstract public function password();
186 // Fallback value is GENDER_MALE.
187 public function isFemale()
189 return $this->gender
== self
::GENDER_FEMALE
;
192 // Fallback value is FORMAT_TEXT.
193 public function isEmailFormatHtml()
195 return $this->email_format
== self
::FORMAT_HTML
;
199 * Other properties are available directly through the $data array, or as
200 * standard object variables, using a getter.
202 public function data()
207 public function __get($name)
209 if (property_exists($this, $name)) {
213 if (isset($this->data
[$name])) {
214 return $this->data
[$name];
220 public function __isset($name)
222 return property_exists($this, $name) ||
isset($this->data
[$name]);
225 public function __unset($name)
227 if (property_exists($this, $name)) {
230 unset($this->data
[$name]);
235 * Fills the object properties using the @p associative array; the intended
236 * user case is to fill the object using SQL obtained arrays.
238 * @param $values Key-value array of user properties.
240 protected function fillFromArray(array $values)
242 // Merge main properties with existing ones.
243 unset($values['data']);
244 foreach ($values as $key => $value) {
245 if (property_exists($this, $key) && !isset($this->$key)) {
246 $this->$key = $value;
250 // Merge all value into the $this->data placeholder.
251 $this->data
= array_merge($this->data
, $values);
255 * Adds properties to the object; this method does not allow the caller to
256 * update core properties (id, ...).
258 * @param $values An associative array of non-core properties.
260 public function addProperties(array $values)
262 foreach ($values as $key => $value) {
263 if (!property_exists($this, $key)) {
264 $this->data
[$key] = $value;
271 * Build the permissions flags for the user.
273 abstract protected function buildPerms();
276 * Check wether the user got the given permission combination.
278 public function checkPerms($perms)
280 if (is_null($this->perm_flags
)) {
283 if (is_null($this->perm_flags
)) {
286 return $this->perm_flags
->hasFlagCombination($perms);
291 * Returns a valid User object built from the @p id and optionnal @p values,
292 * or returns false and calls the callback if the @p id is not valid.
294 public static function get($login, $callback = false
)
296 return User
::getWithValues($login, array(), $callback);
299 public static function getWithValues($login, $values, $callback = false
)
302 $callback = array('User', '_default_user_callback');
306 return new User($login, $values);
307 } catch (UserNotFoundException
$e) {
308 return call_user_func($callback, $login, $e->results
);
312 public static function getWithUID($uid, $callback = false
)
314 return User
::getWithValues(null
, array('uid' => $uid), $callback);
317 // Same as above, but using the silent callback as default.
318 public static function getSilent($login)
320 return User
::getWithValues($login, array(), array('User', '_silent_user_callback'));
323 public static function getSilentWithValues($login, $values)
325 return User
::getWithValues($login, $values, array('User', '_silent_user_callback'));
328 public static function getSilentWithUID($uid)
330 return User
::getWithValues(null
, array('uid' => $uid), array('User', '_silent_user_callback'));
334 * Retrieves User objects corresponding to the @p logins, and eventually
335 * extracts and returns the @p property. If @p strict mode is disabled, it
336 * also includes logins for which no forlife was found (but it still calls
337 * the callback for them).
338 * In all cases, email addresses which are not from the local domains are
341 * @param $logins Array of user logins.
342 * @param $property Property to retrieve from the User objects.
343 * @param $strict Should unvalidated logins be returned as-is or discarded ?
344 * @param $callback Callback to call when a login is unknown to the system.
345 * @return Array of validated user forlife emails.
347 private static function getBulkUserProperties($logins, $property, $strict, $callback)
349 if (!is_array($logins)) {
350 if (strlen(trim($logins)) == 0) {
353 $logins = preg_split("/[; ,\r\n\|]+/", $logins);
358 foreach ($logins as $i => $login) {
359 $login = trim($login);
364 if (($user = User
::get($login, $callback))) {
365 $list[$i] = $user->$property();
366 } else if (!$strict ||
(User
::isForeignEmailAddress($login) && isvalid_email($login))) {
376 * Returns hruid corresponding to the @p logins. See getBulkUserProperties()
379 public static function getBulkHruid($logins, $callback = false
)
381 return self
::getBulkUserProperties($logins, 'login', true
, $callback);
385 * Returns forlife emails corresponding to the @p logins. See
386 * getBulkUserProperties() for details.
388 public static function getBulkForlifeEmails($logins, $strict = true
, $callback = false
)
390 return self
::getBulkUserProperties($logins, 'forlifeEmail', $strict, $callback);
394 * Predefined callbacks for the user lookup; they are called when a given
395 * login is found not to be associated with any valid user. Silent callback
396 * does nothing; default callback is supposed to display an error message,
397 * using the Platal::page() hook.
399 public static function _silent_user_callback($login, $results)
404 private static function stripBadChars($text)
406 return str_replace(array(' ', "'"), array('-', ''),
407 strtolower(stripslashes(replace_accent(trim($text)))));
410 /** Creates a username from a first and last name
411 * @param $firstname User's firstname
412 * @param $lasttname User's lastname
413 * return STRING the corresponding username
415 public static function makeUserName($firstname, $lastname)
417 return self
::stripBadChars($firstname) . '.' . self
::stripBadChars($lastname);
421 * Creates a user forlive identifier from:
422 * @param $firstname User's firstname
423 * @param $lasttname User's lastname
424 * @param $category User's promotion or type of account
426 public static function makeHrid($firstname, $lastname, $category)
428 $cat = self
::stripBadChars($category);
430 Platal
::page()->kill("$category is not a suitable category.");
433 return self
::makeUserName($firstname, $lastname) . '.' . $cat;
436 /** Reformats the firstname so that all letters are in lower case,
437 * except the first letter of each part of the name.
439 public static function fixFirstnameCase($firstname)
441 $firstname = strtolower($firstname);
442 $pieces = explode('-', $firstname);
444 foreach ($pieces as $piece) {
445 $subpieces = explode("'", $piece);
448 foreach ($subpieces as $subpiece) {
449 $usubpieces[] = ucwords($subpiece);
451 $upieces[] = implode("'", $usubpieces);
453 return implode('-', $upieces);
457 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: