- feature: useralbum items in blocks
[project/gallery.git] / gallery_user.inc
1 <?php
2 // $Id$
3
4 /**
5 * gallery.module : gallery_user.inc
6 * User Functions (insert, update, delete, view, details, ...)
7 */
8
9 define(G2MAP_UNKNOWN, 0);
10 define(G2MAP_USER_EXISTS, 1);
11 define(G2MAP_USER_EXISTS_BUT_NEEDS_MAPPING, 2);
12 define(G2MAP_USER_DOES_NOT_EXIST_BUT_IS_MAPPED, 3);
13 define(G2MAP_USER_DOES_NOT_EXIST, 4);
14
15 define(G2USERINFO_NOERROR, 1);
16 define(G2USERINFO_ERROR, 2);
17 define(G2USERINFO_ERROR_MISSING, 3);
18 define(G2USERINFO_ERROR_USERNAME, 4);
19 define(G2USERINFO_ERROR_FULLNAME, 5);
20 define(G2USERINFO_ERROR_FULLNAME_MISSING, 6);
21 define(G2USERINFO_ERROR_FULLNAME_INCORRECT, 7);
22 define(G2USERINFO_ERROR_EMAIL, 8);
23 define(G2USERINFO_ERROR_PASSWORD, 9);
24 define(G2USERINFO_ERROR_GROUPS, 10);
25
26 require_once(drupal_get_path('module', 'gallery') .'/gallery_groups.inc');
27
28 /**
29 * Insert new user
30 */
31 function gallery_user_insert(&$edit, $user) {
32 $user->roles = isset($edit['roles']) ? $edit['roles'] : $user->roles;
33 $user->status = isset($edit['status']) ? $edit['status'] : TRUE;
34
35 gallery_user_modify($user, 'create');
36 }
37
38 /**
39 * Update a user with new information
40 */
41 function gallery_user_update(&$edit, $user) {
42 $user->language = isset($edit['language']) ? $edit['language'] : gallery_get_language($user);
43 $user->pass = !empty($edit['pass']) ? md5($edit['pass']) : $user->pass;
44 $user->status = isset($edit['status']) ? $edit['status'] : $user->status;
45 $user->mail = !empty($edit['mail']) ? $edit['mail'] : $user->mail;
46 $user->roles = isset($edit['roles']) ? $edit['roles'] : $user->roles;
47
48 // Fullname support
49 if (module_exists('profile') && variable_get('gallery_use_fullname', 0)) {
50 $fullname_field = variable_get('gallery_profile_fullname_field', 'profile_fullname');
51 $user->$fullname_field = isset($edit[$fullname_field]) ? $edit[$fullname_field] : $user->$fullname_field;
52 }
53
54 // Username is about to change
55 if ($namechange = (isset($edit['name']) && ($edit['name'] != $user->name))) {
56 // Make sure the original user is up to date
57 gallery_user_modify($user, 'update', TRUE);
58 }
59
60 $user->name = isset($edit['name']) ? $edit['name'] : $user->name;
61
62 if ($namechange) {
63 // Change username
64 gallery_user_modify($user, 'username');
65 }
66 else {
67 // Update user
68 gallery_user_modify($user, 'update', TRUE);
69 }
70 }
71
72 /**
73 * Delete the user from the Gallery
74 */
75 function gallery_user_delete($user) {
76 gallery_user_modify($user, 'delete');
77 }
78
79 /**
80 * Modify (create/update/delete) a user
81 */
82 function gallery_user_modify($user, $action = 'create', $groups = FALSE, $vars = NULL) {
83 if (!_gallery_init(TRUE, $vars)) {
84 return FALSE;
85 }
86
87 // Check for fullname support
88 $fullname_field = variable_get('gallery_profile_fullname_field', 'profile_fullname');
89 $usefullname = module_exists('profile') && variable_get('gallery_use_fullname', 0);
90 $fullname = $usefullname ? (isset($user->$fullname_field) ? $user->$fullname_field : '') : $user->name;
91
92 // Generate random password for G2 if user is blocked
93 $pass = ($user->status) ? $user->pass : user_password(20);
94
95 switch ($action) {
96 case 'username':
97 $ret = GalleryEmbed::updateUser($user->uid,
98 array('username' => $user->name,
99 'fullname' => $fullname,
100 'email' => $user->mail,
101 'language' => gallery_get_language($user),
102 'hashedpassword' => $pass,
103 'hashmethod' => 'md5'));
104 if ($ret) {
105 gallery_error(t('Error updating Gallery user (username changed)'), $ret);
106 return FALSE;
107 }
108 break;
109 case 'create' :
110 case 'update' :
111 // Get map state of the user
112 list($g2_user_state, $g2_user, $ret) = gallery_user_map_state($user);
113 if ($ret) {
114 gallery_error(t('Error determining user map state'), $ret);
115 return FALSE;
116 }
117
118 // Complete user mapping
119 switch ($g2_user_state) {
120 case G2MAP_USER_EXISTS_BUT_NEEDS_MAPPING:
121 // create map entry for the user
122 $ret = GalleryEmbed::addExternalIdMapEntry($user->uid, $g2_user->id, 'GalleryUser');
123 if ($ret) {
124 gallery_error(t('Error creating map entry (ExternlIdMapEntry)'), $ret);
125 return FALSE;
126 }
127 case G2MAP_USER_EXISTS:
128 // Update user (Drupal -> G2)
129 $ret = GalleryEmbed::updateUser($user->uid,
130 array('username' => $user->name,
131 'fullname' => $fullname,
132 'email' => $user->mail,
133 'language' => gallery_get_language($user),
134 'hashedpassword' => $pass,
135 'hashmethod' => 'md5'));
136 if ($ret) {
137 gallery_error(t('Error updating Gallery user'), $ret);
138 return FALSE;
139 }
140 break;
141 case G2MAP_USER_DOES_NOT_EXIST_BUT_IS_MAPPED:
142 // Remove mapping for non-existing user
143 // (also happens if gallery_user_modify() is called with a changed username)
144 $ret = GalleryCoreApi::removeMapEntry('ExternalIdMap', array('externalId' => $user->uid, 'entityType' => 'GalleryUser'));
145 if ($ret) {
146 gallery_error(t('Error removing map entry (ExternlIdMapEntry)'), $ret);
147 return FALSE;
148 }
149 case G2MAP_USER_DOES_NOT_EXIST:
150 // Create new user
151 if (!$user->uid)
152 return FALSE;
153 $ret = GalleryEmbed::createUser($user->uid,
154 array('username' => $user->name,
155 'email' => $user->mail,
156 'fullname' => $fullname,
157 'language' => gallery_get_language($user),
158 'hashedpassword' => $pass,
159 'hashmethod' => 'md5'));
160 if ($ret) {
161 gallery_error(t('Error creating Gallery user'), $ret);
162 return FALSE;
163 }
164 list($ret, $g2_user) = GalleryCoreApi::loadEntityByExternalId($user->uid, 'GalleryUser');
165 if ($ret) {
166 gallery_error(t('Error loading newly created Gallery user'), $ret);
167 return FALSE;
168 }
169 break;
170 }
171 // Update group info
172 _gallery_groups_user($user, $groups);
173 // Admin role mapping
174 $admin_role = variable_get('gallery_user_admin_role', 0);
175 if (($admin_role && in_array($admin_role, array_keys($user->roles))) || ($user->uid == 1)) {
176 // Get G2 admin group id
177 list($ret, $g2_admin_gid) = GalleryCoreApi::getPluginParameter('module', 'core', 'id.adminGroup');
178 if ($ret) {
179 gallery_error(t('Error getting \'adminGroup\' id'), $ret);
180 return FALSE;
181 }
182 // Add user to admin group
183 $ret = GalleryCoreApi::addUserToGroup($g2_user->id, $g2_admin_gid);
184 if ($ret) {
185 gallery_error(t('Error adding user to Gallery group (:gid)',
186 array(':gid' => $g2_admin_gid)), $ret);
187 return FALSE;
188 }
189 }
190 break;
191 case 'delete' :
192 $ret = GalleryEmbed::deleteUser($user->uid);
193 if ($ret) {
194 gallery_error(t('Error deleting Gallery user'), $ret);
195 return FALSE;
196 }
197 break;
198 }
199
200 // Set the 'locked' property for the user
201 if (variable_get('gallery_user_locked', 0) && ($action != 'delete') && ($user->uid > 1)) {
202 list($ret, $g2_user) = GalleryCoreApi::loadEntityByExternalId($user->uid, 'GalleryUser');
203 if ($ret) {
204 gallery_error(t('Error loading Gallery user'), $ret);
205 return FALSE;
206 }
207 if (!$g2_user->locked) {
208 list($ret, $lock_id) = GalleryCoreApi::acquireWriteLock($g2_user->id);
209 if ($ret) {
210 gallery_error(t('Error acquiring write lock'), $ret);
211 return FALSE;
212 }
213 $g2_user->setLocked(TRUE);
214 if ($g2_user->save()) {
215 gallery_error(t('Locking user account failed'), $ret);
216 return FALSE;
217 }
218 }
219 }
220
221 GalleryEmbed::done();
222 return TRUE;
223 }
224
225 /**
226 * Sync user info for $uid
227 */
228 function _gallery_user_sync($uid) {
229 $user = user_load(array('uid' => $uid));
230 gallery_user_modify($user, 'update', TRUE);
231 }
232
233 /**
234 * View Gallery user details for a specific user
235 */
236 function gallery_user_view($user) {
237 if (variable_get('gallery_user_hide_profile', 0) || !_gallery_init(TRUE)) {
238 return;
239 }
240 // User album status
241 if ($useralbum = gallery_user_useralbum($user->uid)) {
242 $form['gallery_view_user_album'] = array(
243 'value' => l(t('User Album'), $useralbum),
244 'class' => 'send-message');
245 }
246 else {
247 $form['gallery_view_user_album'] = array(
248 'value' => t('User has not created an album yet'),
249 'class' => 'send-message');
250 }
251 // Sync/Map status info
252 $g2_userinfo = gallery_user_map_info($user);
253 if (($g2_userinfo['status']) && (user_access('administer users'))) {
254 $form['gallery_view_user'] = array(
255 'title' => t('Gallery2-Drupal Sync Status'),
256 'value' => implode(',<br />', gallery_user_map_info_status($g2_userinfo['status']))
257 );
258 }
259
260 GalleryEmbed::done();
261 if (!empty($form)) {
262 return array(t('Gallery2') => $form);
263 }
264 }
265
266 /**
267 * Create link to user album
268 */
269 function gallery_user_useralbum($uid = NULL, $link = TRUE) {
270 if (!_gallery_init(TRUE)) {
271 return FALSE;
272 }
273 // Load G2 user
274 global $user;
275 list($ret, $g2_user) = GalleryCoreApi::loadEntityByExternalId(isset($uid) ? $uid : $user->uid, 'GalleryUser');
276 if ($ret) {
277 if (!($ret->getErrorCode() & ERROR_MISSING_OBJECT)) {
278 gallery_error(t('Error loading Gallery user'), $ret);
279 return FALSE;
280 }
281 }
282 // User album status
283 if ($g2_user && (gallery_single_plugin_status('useralbum') == GALLERY_PLUGIN_ENABLED)) {
284 // Fetch user album id
285 list($ret, $album_id) = GalleryCoreApi::getPluginParameter('module', 'useralbum', 'albumId', $g2_user->id);
286 if ($ret) {
287 gallery_error(t('Error fetching user album id'), $ret);
288 return FALSE;
289 }
290 // Generate link to user album
291 if (is_numeric($album_id) && $album_id > 0) {
292 return $link ? gallery_generate_url(array('view' => 'core.ShowItem', 'itemId' => $album_id), FALSE) : $album_id;
293 }
294 }
295
296 return FALSE;
297 }
298
299 /**
300 * Get info about user map status
301 */
302 function gallery_user_map_info($user, $noerror_status = TRUE) {
303 $g2_userinfo = array('g2_id' => -1, 'status' => array());
304 // User map entry
305 $ret = GalleryEmbed::isExternalIdMapped($user->uid, 'GalleryUser');
306 if ($ret && !($ret->getErrorCode() & ERROR_MISSING_OBJECT)) {
307 $g2_userinfo['status'][] = G2USERINFO_ERROR_MISSING;
308 return $g2_userinfo;
309 }
310 // But user not available
311 list($ret, $g2_user) = GalleryCoreApi::loadEntityByExternalId($user->uid, 'GalleryUser');
312 if ($ret) {
313 $g2_userinfo['status'][] = G2USERINFO_ERROR_MISSING;
314 return $g2_userinfo;
315 }
316 // Username
317 $g2_userinfo['g2_id'] = $g2_user->id;
318 if ($g2_user->userName != $user->name) {
319 $g2_userinfo['status'][] = G2USERINFO_ERROR_USERNAME;
320 }
321 // Fullname
322 if (module_exists('profile') && variable_get('gallery_use_fullname', 0)) {
323 $fullname_field = variable_get('gallery_profile_fullname_field', 'profile_fullname');
324 $fullname_result = db_query("SELECT v.value FROM {profile_values} v INNER JOIN {profile_fields} f ON v.fid = f.fid AND v.uid = %d WHERE f.name = '%s'", $user->uid, $fullname_field);
325 $fullname = db_fetch_object($fullname_result);
326 $fullname = $fullname->value;
327 if ($g2_user->fullName != $fullname) {
328 $g2_userinfo['status'][] = G2USERINFO_ERROR_FULLNAME;
329 }
330 elseif (!$fullname) {
331 $g2_userinfo['status'][] = G2USERINFO_ERROR_FULLNAME_MISSING;
332 }
333 }
334 else {
335 if ($g2_user->fullName != $user->name) {
336 $g2_userinfo['status'][] = G2USERINFO_ERROR_FULLNAME_INCORRECT;
337 }
338 }
339 // Email adress
340 if ($g2_user->email != $user->mail) {
341 $g2_userinfo['status'][] = G2USERINFO_ERROR_EMAIL;
342 }
343 // Password
344 if ($user->status && ($g2_user->hashedPassword != $user->pass)) {
345 $g2_userinfo['status'][] = G2USERINFO_ERROR_PASSWORD;
346 }
347 // Roles/Groups
348 if (!gallery_groups_map_info($g2_user, $user)) {
349 $g2_userinfo['status'][] = G2USERINFO_ERROR_GROUPS;
350 }
351 // Overall sync status
352 if ($noerror_status && !count($g2_userinfo['status'])) {
353 $g2_userinfo['status'][] = G2USERINFO_NOERROR;
354 }
355
356 return $g2_userinfo;
357 }
358
359 /**
360 * Get string representation of the use map status
361 */
362 function gallery_user_map_info_status($info = array(), $format = TRUE) {
363 $info_map = array(
364 G2USERINFO_NOERROR => t('OK'),
365 G2USERINFO_ERROR => t('Any Error'),
366 G2USERINFO_ERROR_MISSING => t('Missing from G2'),
367 G2USERINFO_ERROR_USERNAME => t('Different Usernames'),
368 G2USERINFO_ERROR_FULLNAME => t('Different Full Names'),
369 G2USERINFO_ERROR_FULLNAME_MISSING => t('G2 Full Name missing'),
370 G2USERINFO_ERROR_FULLNAME_INCORRECT => t('G2 Full Name incorrect'),
371 G2USERINFO_ERROR_EMAIL => t('Different E-Mails'),
372 G2USERINFO_ERROR_PASSWORD => t('Different Passwords'),
373 G2USERINFO_ERROR_GROUPS => t('Roles <> Groups')
374 );
375
376 if ($format) {
377 $status = array();
378 if (!count($info)) {
379 $info[] = G2USERINFO_NOERROR;
380 }
381 foreach ($info as $key => $value) {
382 $status[] = $info_map[$value];
383 }
384 return $status;
385 }
386
387 return $info_map;
388 }
389
390 /**
391 * Get state of user mapping
392 */
393 function gallery_user_map_state($user) {
394 // See if user already exists in G2
395 list($ret, $g2_user) = GalleryCoreApi::fetchUserByUsername($user->name);
396 if (!$ret) {
397 // User is in G2, so map the user if needed
398 $ret2 = GalleryEmbed::isExternalIdMapped($user->uid, 'GalleryUser');
399 if ($ret2) {
400 if ($ret2->getErrorCode() & ERROR_MISSING_OBJECT) {
401 return array(G2MAP_USER_EXISTS_BUT_NEEDS_MAPPING, $g2_user, NULL);
402 }
403 else {
404 // Some other error
405 return array(G2MAP_UNKNOWN, $g2_user, $ret2);
406 }
407 }
408 else {
409 return array(G2MAP_USER_EXISTS, $g2_user, NULL);
410 }
411 }
412 elseif ($ret->getErrorCode() & ERROR_MISSING_OBJECT) {
413 // User does not yet exist in G2
414 // Check if the externalID was mapped (it should not be)
415 $ret2 = GalleryEmbed::isExternalIdMapped($user->uid, 'GalleryUser');
416 if ($ret2) {
417 if ($ret2->getErrorCode() & ERROR_MISSING_OBJECT) {
418 return array(G2MAP_USER_DOES_NOT_EXIST, $g2_user, NULL);
419 }
420 else {
421 // Some other error
422 return array(G2MAP_UNKNOWN, $g2_user, $ret2);
423 }
424 }
425 else {
426 // User is mapped
427 return array(G2MAP_USER_DOES_NOT_EXIST_BUT_IS_MAPPED, $g2_user, NULL);
428 }
429 }
430 else {
431 return array(NULL, $g2_user, $ret);
432 }
433 }
434
435 /**
436 * Import Gallery users into Drupal
437 */
438 function _gallery_user_import($g2_users = array()) {
439 $errors = array();
440 // Anonymous user id
441 list($ret, $guest) = GalleryCoreApi::getPluginParameter('module', 'core', 'id.anonymousUser');
442 if (!$ret) {
443 unset($g2_users[$guest]);
444 }
445 $g2_groups_map = _gallery_groups_map();
446 if (!($g2_extIdMap = _gallery_user_map(array_keys($g2_users)))) {
447 return FALSE;
448 }
449 gallery_debug($g2_users, t('G2 Users'));
450 gallery_debug($g2_extIdMap, t('G2 ExternalIdMap'));
451 // Iterate over G2 users
452 foreach ($g2_users as $g2_id => $g2_username) {
453 $new_user = !array_key_exists($g2_id, $g2_extIdMap);
454 list($ret, $g2_user) = GalleryCoreApi::fetchUserByUsername($g2_username);
455 if ($ret) {
456 gallery_error(t('Error fetching user by username (:name)',
457 array(':name' => $g2_username)), $ret);
458 return FALSE;
459 }
460 // Collect user details and validate the values (name and mail)
461 $values = array();
462 $values['name'] = $g2_user->userName;
463 if ($error = user_validate_name($values['name'])) {
464 $errors[] = t('G2 User Import (uid: :uid, name: \':name\'): !error',
465 array(':uid' => $g2_id, ':name' => $values['name'], '!error' => $error));
466 continue;
467 }
468 $values['fullname'] = $g2_user->fullName;
469 $values['pass'] = $g2_user->hashedPassword;
470 $values['mail'] = $g2_user->email;
471 if ($error = user_validate_mail($values['mail'])) {
472 $errors[] = t('G2 User Import (uid: :uid, mail: \':mail\'): !error',
473 array(':mail' => $values['mail'], ':uid' => $g2_id, '!error' => $error));
474 continue;
475 }
476 else if ($new_user && db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != 0 AND LOWER(mail) = LOWER('%s')", $values['mail'])) > 0) {
477 $errors[] = t('G2 User Import (uid: :uid): The e-mail address :mail is already registered.',
478 array(':uid' => $g2_id, ':mail' => $values['mail']));
479 continue;
480 }
481 $values['language'] = $g2_user->language;
482 $values['created'] = $g2_user->creationTimestamp;
483 $values['roles'] = array();
484 list($ret, $g2_groups) = GalleryCoreApi::fetchGroupsForUser($g2_user->id);
485 if ($ret) {
486 gallery_error(t('Error fetching groups for user (uid: :uid)',
487 array(':uid' => $g2_id)), $ret);
488 return FALSE;
489 }
490 foreach ($g2_groups as $g2_gid => $g2_groupname) {
491 if (isset($g2_groups_map[$g2_gid])) {
492 $values['roles'][$g2_groups_map[$g2_gid]] = $g2_groupname;
493 }
494 }
495 // Is the user blocked in G2
496 list($ret, $g2_user_blocked) = GalleryCoreApi::isDisabledUsername($g2_username);
497 if ($ret) {
498 gallery_error(t('Error calling isDisabledUsername() for \':name\'',
499 array(':name' => $g2_username)), $ret);
500 return FALSE;
501 }
502 $values['status'] = !$g2_user_blocked;
503 // Create new Drupal user (this will also override the G2 user
504 // during hook_user, but there is no 'clean' way to avoid this)
505 if ($new_user) {
506 $values['notify'] = FALSE;
507 if (!($user = user_save('', $values))) {
508 $errors[] = t('Error creating Drupal user for G2 user (uid: :uid)',
509 array(':uid' => $g2_id));
510 continue;
511 }
512 }
513 else {
514 $user = new stdClass();
515 $user->uid = $g2_extIdMap[$g2_id];
516 }
517 // Override user details if requested (or for new users)
518 if ($user->uid && ($new_user || variable_get('gallery_user_import_override', 0))) {
519 // Fullname support
520 if (module_exists('profile') && variable_get('gallery_use_fullname', 0)) {
521 $fullname_category = variable_get('gallery_profile_fullname_category', 'Personal Information');
522 $fullname_field = variable_get('gallery_profile_fullname_field', 'profile_fullname');
523 $values[$fullname_field] = $values['fullname'];
524 profile_save_profile($values, $user, $fullname_category);
525 }
526 // Override password directly (needed because G2 only provides the md5 hash)
527 _gallery_user_drupal_password($user->uid, $values['pass']);
528 unset($values['pass']);
529 // Update Drupal user with G2 user details (invokes hook_user)
530 user_save(user_load(array('uid' => $user->uid)), $values);
531 }
532 }
533
534 if (count($errors)) {
535 if (isset($_SESSION['gallery_user_progress_messages'])) {
536 $_SESSION['gallery_user_progress_messages'] += $errors;
537 }
538 else {
539 drupal_set_message(theme('item_list', $errors, t('The following invalid user items were skipped:')), 'error');
540 }
541 }
542
543 return TRUE;
544 }
545
546 /**
547 * Override Drupal user password
548 */
549 function _gallery_user_drupal_password($uid, $pass) {
550 db_query("UPDATE {users} SET pass = '%s' WHERE uid = %d", $pass, $uid);
551 }
552
553 /**
554 * Fetch 'GalleryUser' entries from G2 'ExternalIdMap'
555 * g2Id => externalId (default)
556 */
557 function _gallery_user_map($ids = array(), $inverse = FALSE) {
558 $match = array('entityType' => 'GalleryUser');
559 if (count($ids) > 0) {
560 if ($inverse) {
561 $match['externalId'] = $ids;
562 }
563 else {
564 $match['entityId'] = $ids;
565 }
566 }
567 // Fetch the map entries
568 list($ret, $resultMap) = GalleryCoreApi::getMapEntry('ExternalIdMap', array('externalId', 'entityId'), $match);
569 if ($ret) {
570 gallery_error(t('Error fetching \'GalleryUser\' entries from \'ExternalIdMap\''), $ret);
571 return NULL;
572 }
573 // Iterate over the results
574 $g2_extIdMap = array();
575 while (($row = $resultMap->nextResult()) !== FALSE) {
576 $g2_extIdMap[($inverse ? $row[0] : $row[1])] = ($inverse ? $row[1] : $row[0]);
577 }
578
579 return $g2_extIdMap;
580 }
581
582 /**
583 * Fetch all existing Drupal users (uid => username)
584 */
585 function _gallery_user_drupal_users() {
586 $users = array();
587 $result = db_query("SELECT uid, name FROM {users} WHERE uid > 0");
588 while ($user = db_fetch_object($result)) {
589 $users[$user->uid] = $user->name;
590 }
591
592 return $users;
593 }
594
595 ?>