Adds type information to the subclass-defined method 'fetch' of PlFeed, to ensure...
[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 enumerations.
46 */
47 const GENDER_FEMALE = true;
48 const GENDER_MALE = false;
49 const FORMAT_HTML = "html";
50 const FORMAT_TEXT = "text";
51
52 /**
53 * User data storage.
54 * By convention, null means the information hasn't been fetched yet, and
55 * false means the information is not available.
56 */
57 protected $user_id = null;
58 protected $hruid = null;
59
60 // User main email aliases (forlife is the for-life email address, bestalias
61 // is user-chosen preferred email address).
62 protected $forlife = null;
63 protected $bestalias = null;
64
65 // Display name is user-chosen name to display (eg. in "Welcome
66 // <display name> !"), while full name is the official full name.
67 protected $display_name = null;
68 protected $full_name = null;
69 protected $promo = null;
70
71 // Other important parameters used when sending emails.
72 protected $gender = null; // Acceptable values are GENDER_MALE and GENDER_FEMALE
73 protected $email_format = null; // Acceptable values are FORMAT_HTML and FORMAT_TEXT
74
75 // Permissions
76 protected $perms = null;
77 protected $perm_flags = null;
78
79 // Other properties are listed in this key-value hash map.
80 protected $data = array();
81
82 /**
83 * Constructs the PlUser object from an identifier (any identifier which is
84 * understood by getLogin() implementation).
85 *
86 * @param $login An user login.
87 * @param $values List of known user properties.
88 */
89 public function __construct($login, $values = array())
90 {
91 $this->fillFromArray($values);
92
93 // If the user id was not part of the known values, determines it from
94 // the login.
95 if (!$this->user_id) {
96 $this->user_id = $this->getLogin($login);
97 }
98
99 // Preloads main properties (assumes the loader will lazily get them
100 // from variables already set in the object).
101 $this->loadMainFields();
102 }
103
104 /**
105 * Get the canonical user id for the @p login.
106 *
107 * @param $login An user login.
108 * @return The canonical user id.
109 * @throws UserNotFoundException when login is not found.
110 */
111 abstract protected function getLogin($login);
112
113 /**
114 * Loads the main properties (hruid, forlife, bestalias, ...) from the
115 * database. Should return immediately when the properties are already
116 * available.
117 */
118 abstract protected function loadMainFields();
119
120 /**
121 * Accessors to the main properties, ie. those available as top level
122 * object variables.
123 */
124 public function id()
125 {
126 return $this->user_id;
127 }
128
129 public function login()
130 {
131 return $this->hruid;
132 }
133
134 public function bestEmail()
135 {
136 return $this->bestalias;
137 }
138 public function forlifeEmail()
139 {
140 return $this->forlife;
141 }
142
143 public function displayName()
144 {
145 return $this->display_name;
146 }
147 public function fullName()
148 {
149 return $this->full_name;
150 }
151
152 public function promo()
153 {
154 return $this->promo;
155 }
156
157 // Fallback value is GENDER_MALE.
158 public function isFemale()
159 {
160 return $this->gender == self::GENDER_FEMALE;
161 }
162
163 // Fallback value is FORMAT_TEXT.
164 public function isEmailFormatHtml()
165 {
166 return $this->email_format == self::FORMAT_HTML;
167 }
168
169 /**
170 * Other properties are available directly through the $data array, or as
171 * standard object variables, using a getter.
172 */
173 public function data()
174 {
175 return $this->data;
176 }
177
178 public function __get($name)
179 {
180 if (property_exists($this, $name)) {
181 return $this->$name;
182 }
183
184 if (isset($this->data[$name])) {
185 return $this->data[$name];
186 }
187
188 return null;
189 }
190
191 public function __isset($name)
192 {
193 return property_exists($this, $name) || isset($this->data[$name]);
194 }
195
196 /**
197 * Fills the object properties using the @p associative array; the intended
198 * user case is to fill the object using SQL obtained arrays.
199 *
200 * @param $values Key-value array of user properties.
201 */
202 protected function fillFromArray(array $values)
203 {
204 // Merge main properties with existing ones.
205 unset($values['data']);
206 foreach ($values as $key => $value) {
207 if (property_exists($this, $key) && !isset($this->$key)) {
208 $this->$key = $value;
209 }
210 }
211
212 // Merge all value into the $this->data placeholder.
213 $this->data = array_merge($this->data, $values);
214 }
215
216 /**
217 * Adds properties to the object; this method does not allow the caller to
218 * update core properties (id, ...).
219 *
220 * @param $values An associative array of non-core properties.
221 */
222 public function addProperties(array $values)
223 {
224 foreach ($values as $key => $value) {
225 if (!property_exists($this, $key)) {
226 $this->data[$key] = $value;
227 }
228 }
229 }
230
231
232 /**
233 * Build the permissions flags for the user.
234 */
235 abstract protected function buildPerms();
236
237 /**
238 * Check wether the user got the given permission combination.
239 */
240 public function checkPerms($perms)
241 {
242 if (is_null($this->perm_flags)) {
243 $this->buildPerms();
244 }
245 if (is_null($this->perm_flags)) {
246 return false;
247 }
248 return $this->perm_flags->hasFlagCombination($perms);
249 }
250
251
252 /**
253 * Returns a valid User object built from the @p id and optionnal @p values,
254 * or returns false and calls the callback if the @p id is not valid.
255 */
256 public static function get($login, $callback = false)
257 {
258 return User::getWithValues($login, array(), $callback);
259 }
260
261 public static function getWithValues($login, $values, $callback = false)
262 {
263 if (!$callback) {
264 $callback = array('User', '_default_user_callback');
265 }
266
267 try {
268 return new User($login, $values);
269 } catch (UserNotFoundException $e) {
270 return call_user_func($callback, $login, $e->results);
271 }
272 }
273
274 // Same as above, but using the silent callback as default.
275 public static function getSilent($login)
276 {
277 return User::getWithValues($login, array(), array('User', '_silent_user_callback'));
278 }
279
280 public static function getSilentWithValues($login, $values)
281 {
282 return User::getWithValues($login, $values, array('User', '_silent_user_callback'));
283 }
284
285 /**
286 * Retrieves User objects corresponding to the @p logins, and eventually
287 * extracts and returns the @p property. If @p strict mode is disabled, it
288 * also includes logins for which no forlife was found (but it still calls
289 * the callback for them).
290 * In all cases, email addresses which are not from the local domains are
291 * kept.
292 *
293 * @param $logins Array of user logins.
294 * @param $property Property to retrieve from the User objects.
295 * @param $strict Should unvalidated logins be returned as-is or discarded ?
296 * @param $callback Callback to call when a login is unknown to the system.
297 * @return Array of validated user forlife emails.
298 */
299 private static function getBulkUserProperties($logins, $property, $strict, $callback)
300 {
301 if (!is_array($logins)) {
302 if (strlen(trim($logins)) == 0) {
303 return null;
304 }
305 $logins = split("[; ,\r\n\|]+", $logins);
306 }
307
308 if ($logins) {
309 $list = array();
310 foreach ($logins as $i => $login) {
311 $login = trim($login);
312 if (empty($login)) {
313 continue;
314 }
315
316 if (($user = User::get($login, $callback))) {
317 $list[$i] = $user->$property();
318 } else if (!$strict || User::isForeignEmailAddress($login)) {
319 $list[$i] = $login;
320 }
321 }
322 return $list;
323 }
324 return null;
325 }
326
327 /**
328 * Returns hruid corresponding to the @p logins. See getBulkUserProperties()
329 * for details.
330 */
331 public static function getBulkHruid($logins, $callback = false)
332 {
333 return self::getBulkUserProperties($logins, 'login', true, $callback);
334 }
335
336 /**
337 * Returns forlife emails corresponding to the @p logins. See
338 * getBulkUserProperties() for details.
339 */
340 public static function getBulkForlifeEmails($logins, $strict = true, $callback = false)
341 {
342 return self::getBulkUserProperties($logins, 'forlifeEmail', $strict, $callback);
343 }
344
345 /**
346 * Predefined callbacks for the user lookup; they are called when a given
347 * login is found not to be associated with any valid user. Silent callback
348 * does nothing; default callback is supposed to display an error message,
349 * using the Platal::page() hook.
350 */
351 public static function _silent_user_callback($login, $results)
352 {
353 return;
354 }
355
356 abstract public static function _default_user_callback($login, $results);
357
358 /**
359 * Determines if the @p login is an email address, and an email address not
360 * served locally by plat/al.
361 */
362 abstract public static function isForeignEmailAddress($email);
363 }
364
365 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
366 ?>