5b36897f6111a9942806096fe23590bda7587fd4
[platal.git] / classes / pluser.php
1 <?php
2 /***************************************************************************
3 * Copyright (C) 2003-2008 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 /**
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).
25 */
26 class UserNotFoundException extends Exception
27 {
28 public function __construct($results = array())
29 {
30 $this->results = $results;
31 parent::__construct();
32 }
33 }
34
35 /**
36 * Represents an user of plat/al (without any further assumption), with a
37 * special focus on always-used properties (identification fields, display name,
38 * forlife/bestalias emails, ...).
39 * NOTE: each implementation of plat/al-code MUST subclass PlUser, and name it
40 * 'User'.
41 */
42 abstract class PlUser
43 {
44 /**
45 * User data storage.
46 * By convention, null means the information hasn't been fetched yet, and
47 * false means the information is not available.
48 */
49 protected $user_id = null;
50 protected $hruid = null;
51
52 // User main email aliases (forlife is the for-life email address, bestalias
53 // is user-chosen preferred email address).
54 protected $forlife = null;
55 protected $bestalias = null;
56
57 // Display name is user-chosen name to display (eg. in "Welcome
58 // <display name> !"), while full name is the official full name.
59 protected $display_name = null;
60 protected $full_name = null;
61 protected $promo = null;
62
63 // Other properties are listed in this key-value hash map.
64 protected $data = array();
65
66 /**
67 * Constructs the PlUser object from an identifier (any identifier which is
68 * understood by getLogin() implementation).
69 *
70 * @param $login An user login.
71 * @param $values List of known user properties.
72 */
73 public function __construct($login, $values = array())
74 {
75 $this->fillFromArray($values);
76
77 // If the user id was not part of the known values, determines it from
78 // the login.
79 if (!$this->user_id) {
80 $this->user_id = $this->getLogin($login);
81 }
82
83 // Preloads main properties (assumes the loader will lazily get them
84 // from variables already set in the object).
85 $this->loadMainFields();
86 }
87
88 /**
89 * Get the canonical user id for the @p login.
90 *
91 * @param $login An user login.
92 * @return The canonical user id.
93 * @throws UserNotFoundException when login is not found.
94 */
95 abstract protected function getLogin($login);
96
97 /**
98 * Loads the main properties (hruid, forlife, bestalias, ...) from the
99 * database. Should return immediately when the properties are already
100 * available.
101 */
102 abstract protected function loadMainFields();
103
104 /**
105 * Accessors to the main properties, ie. those available as top level
106 * object variables.
107 */
108 public function id() { return $this->user_id; }
109 public function login() { return $this->hruid; }
110
111 public function bestEmail() { return $this->bestalias; }
112 public function forlifeEmail() { return $this->forlife; }
113
114 public function displayName() { return $this->display_name; }
115 public function fullName() { return $this->full_name; }
116
117 /**
118 * Other properties are available directly through the $data array, or as
119 * standard object variables, using a getter.
120 */
121 public function data() { return $this->data; }
122
123 public function __get($name)
124 {
125 if (isset($this->$name)) {
126 return $this->$name;
127 }
128
129 if (isset($this->data[$name])) {
130 return $this->data[$name];
131 }
132
133 return null;
134 }
135
136 public function __isset($name)
137 {
138 return isset($this->$name) || isset($this->data[$name]);
139 }
140
141 /**
142 * Fills the object properties using the @p associative array; the intended
143 * user case is to fill the object using SQL obtained arrays.
144 *
145 * @param $values Key-value array of user properties.
146 */
147 protected function fillFromArray(array $values)
148 {
149 // Merge main properties with existing ones.
150 unset($values['data']);
151 foreach ($values as $key => $value) {
152 if (property_exists($this, $key) && !isset($this->$key)) {
153 $this->$key = $value;
154 }
155 }
156
157 // Merge all value into the $this->data placeholder.
158 $this->data = array_merge($this->data, $values);
159 }
160
161
162 /**
163 * Returns a valid User object built from the @p id and optionnal @p values,
164 * or returns false and calls the callback if the @p id is not valid.
165 */
166 public static function get($login, $callback = false)
167 {
168 return User::getWithValues($login, array(), $callback);
169 }
170
171 public static function getWithValues($login, $values, $callback = false)
172 {
173 if (!$callback) {
174 $callback = array('User', '_default_user_callback');
175 }
176
177 try {
178 return new User($login, $values);
179 } catch (UserNotFoundException $e) {
180 return call_user_func($callback, $login, $e->results);
181 }
182 }
183
184 // Same as above, but using the silent callback as default.
185 public static function getSilent($login)
186 {
187 return User::getWithValues($login, array(), array('User', '_silent_user_callback'));
188 }
189
190 public static function getSilentWithValues($login, $values)
191 {
192 return User::getWithValues($login, $values, array('User', '_silent_user_callback'));
193 }
194
195 /**
196 * Returns forlife emails corresponding to the @p logins. If @p strict mode
197 * is disabled, it also includes login for which no forlife was found (but
198 * still call the callback for them).
199 * In all cases, email addresses which are not from the local domains are
200 * kept.
201 *
202 * @param $logins Array of user logins.
203 * @param $strict Should unvalidated logins be returned as-is or discarded ?
204 * @param $callback Callback to call when a login is unknown to the system.
205 * @return Array of validated user forlife emails.
206 */
207 public static function getBulkForlifeEmails($logins, $strict = true, $callback = false)
208 {
209 if (!is_array($logins)) {
210 if (strlen(trim($logins)) == 0) {
211 return null;
212 }
213 $logins = split("[; ,\r\n\|]+", $logins);
214 }
215
216 if ($logins) {
217 $list = array();
218 foreach ($logins as $i => $login) {
219 $login = trim($login);
220 if (empty($login)) {
221 continue;
222 }
223
224 if (($user = User::get($login, $callback))) {
225 $list[$i] = $user->forlifeEmail();
226 } else if (!$strict || User::isForeignEmailAddress($login)) {
227 $list[$i] = $login;
228 }
229 }
230 return $list;
231 }
232 return null;
233 }
234
235 /**
236 * Predefined callbacks for the user lookup; they are called when a given
237 * login is found not to be associated with any valid user. Silent callback
238 * does nothing; default callback is supposed to display an error message,
239 * using the Platal::page() hook.
240 */
241 public static function _silent_user_callback($login, $results)
242 {
243 return;
244 }
245
246 abstract public static function _default_user_callback($login, $results);
247
248 /**
249 * Determines if the @p login is an email address, and an email address not
250 * served locally by plat/al.
251 */
252 abstract public static function isForeignEmailAddress($email);
253 }
254
255 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
256 ?>