Adds fields 'gender' and 'email_format' to the PlUser class.
[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 (isset($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 isset($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 /**
218 * Build the permissions flags for the user.
219 */
220 abstract protected function buildPerms();
221
222 /**
223 * Check wether the user got the given permission combination.
224 */
225 public function checkPerms($perms)
226 {
227 if (is_null($this->perm_flags)) {
228 $this->buildPerms();
229 }
230 if (is_null($this->perm_flags)) {
231 return false;
232 }
233 return $this->perm_flags->hasFlagCombination($perms);
234 }
235
236
237 /**
238 * Returns a valid User object built from the @p id and optionnal @p values,
239 * or returns false and calls the callback if the @p id is not valid.
240 */
241 public static function get($login, $callback = false)
242 {
243 return User::getWithValues($login, array(), $callback);
244 }
245
246 public static function getWithValues($login, $values, $callback = false)
247 {
248 if (!$callback) {
249 $callback = array('User', '_default_user_callback');
250 }
251
252 try {
253 return new User($login, $values);
254 } catch (UserNotFoundException $e) {
255 return call_user_func($callback, $login, $e->results);
256 }
257 }
258
259 // Same as above, but using the silent callback as default.
260 public static function getSilent($login)
261 {
262 return User::getWithValues($login, array(), array('User', '_silent_user_callback'));
263 }
264
265 public static function getSilentWithValues($login, $values)
266 {
267 return User::getWithValues($login, $values, array('User', '_silent_user_callback'));
268 }
269
270 /**
271 * Retrieves User objects corresponding to the @p logins, and eventually
272 * extracts and returns the @p property. If @p strict mode is disabled, it
273 * also includes logins for which no forlife was found (but it still calls
274 * the callback for them).
275 * In all cases, email addresses which are not from the local domains are
276 * kept.
277 *
278 * @param $logins Array of user logins.
279 * @param $property Property to retrieve from the User objects.
280 * @param $strict Should unvalidated logins be returned as-is or discarded ?
281 * @param $callback Callback to call when a login is unknown to the system.
282 * @return Array of validated user forlife emails.
283 */
284 private static function getBulkUserProperties($logins, $property, $strict, $callback)
285 {
286 if (!is_array($logins)) {
287 if (strlen(trim($logins)) == 0) {
288 return null;
289 }
290 $logins = split("[; ,\r\n\|]+", $logins);
291 }
292
293 if ($logins) {
294 $list = array();
295 foreach ($logins as $i => $login) {
296 $login = trim($login);
297 if (empty($login)) {
298 continue;
299 }
300
301 if (($user = User::get($login, $callback))) {
302 $list[$i] = $user->$property();
303 } else if (!$strict || User::isForeignEmailAddress($login)) {
304 $list[$i] = $login;
305 }
306 }
307 return $list;
308 }
309 return null;
310 }
311
312 /**
313 * Returns hruid corresponding to the @p logins. See getBulkUserProperties()
314 * for details.
315 */
316 public static function getBulkHruid($logins, $callback = false)
317 {
318 return self::getBulkUserProperties($logins, 'login', true, $callback);
319 }
320
321 /**
322 * Returns forlife emails corresponding to the @p logins. See
323 * getBulkUserProperties() for details.
324 */
325 public static function getBulkForlifeEmails($logins, $strict = true, $callback = false)
326 {
327 return self::getBulkUserProperties($logins, 'forlifeEmail', $strict, $callback);
328 }
329
330 /**
331 * Predefined callbacks for the user lookup; they are called when a given
332 * login is found not to be associated with any valid user. Silent callback
333 * does nothing; default callback is supposed to display an error message,
334 * using the Platal::page() hook.
335 */
336 public static function _silent_user_callback($login, $results)
337 {
338 return;
339 }
340
341 abstract public static function _default_user_callback($login, $results);
342
343 /**
344 * Determines if the @p login is an email address, and an email address not
345 * served locally by plat/al.
346 */
347 abstract public static function isForeignEmailAddress($email);
348 }
349
350 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
351 ?>