/[drupal]/drupal/modules/profile/profile.module
ViewVC logotype

Contents of /drupal/modules/profile/profile.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.281 - (show annotations) (download) (as text)
Sun Nov 1 21:26:44 2009 UTC (3 weeks, 5 days ago) by webchick
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-10, HEAD
Changes since 1.280: +2 -2 lines
File MIME type: text/x-php
#192056 by effulgentsia, Dave Cohen, andypost, hswong3i, geodaniel, pwolanin, and dahacouk: Ensure user's raw login name is never output directly.
1 <?php
2 // $Id: profile.module,v 1.280 2009/10/23 22:24:16 webchick Exp $
3
4 /**
5 * @file
6 * Support for configurable user profiles.
7 */
8
9 /**
10 * Private field, content only available to privileged users.
11 */
12 define('PROFILE_PRIVATE', 1);
13
14 /**
15 * Public field, content shown on profile page but not used on member list pages.
16 */
17 define('PROFILE_PUBLIC', 2);
18
19 /**
20 * Public field, content shown on profile page and on member list pages.
21 */
22 define('PROFILE_PUBLIC_LISTINGS', 3);
23
24 /**
25 * Hidden profile field, only accessible by administrators, modules and themes.
26 */
27 define('PROFILE_HIDDEN', 4);
28
29 /**
30 * Implement hook_help().
31 */
32 function profile_help($path, $arg) {
33 switch ($path) {
34 case 'admin/help#profile':
35 $output = '<p>' . t('The profile module allows custom fields (such as country, full name, or age) to be defined and displayed in the <em>My Account</em> section. This permits users of a site to share more information about themselves, and can help community-based sites organize users around specific information.') . '</p>';
36 $output .= '<p>' . t('The following types of fields can be added to a user profile:') . '</p>';
37 $output .= '<ul><li>' . t('single-line textfield') . '</li>';
38 $output .= '<li>' . t('multi-line textfield') . '</li>';
39 $output .= '<li>' . t('checkbox') . '</li>';
40 $output .= '<li>' . t('list selection') . '</li>';
41 $output .= '<li>' . t('freeform list') . '</li>';
42 $output .= '<li>' . t('URL') . '</li>';
43 $output .= '<li>' . t('date') . '</li></ul>';
44 $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@profile">Profile module</a>.', array('@profile' => 'http://drupal.org/handbook/modules/profile/')) . '</p>';
45 return $output;
46 case 'admin/config/people/profile':
47 return '<p>' . t("This page displays a list of the existing custom profile fields to be displayed on a user's <em>My Account</em> page. To provide structure, similar or related fields may be placed inside a category. To add a new category (or edit an existing one), edit a profile field and provide a new category name. Remember that your changes will not be saved until you click the <em>Save configuration</em> button at the bottom of the page.") . '</p>';
48 }
49 }
50
51 /**
52 * Implement hook_theme().
53 */
54 function profile_theme() {
55 return array(
56 'profile_block' => array(
57 'variables' => array('account' => NULL, 'fields' => array()),
58 'template' => 'profile-block',
59 ),
60 'profile_listing' => array(
61 'variables' => array('account' => NULL, 'fields' => array()),
62 'template' => 'profile-listing',
63 ),
64 'profile_wrapper' => array(
65 'variables' => array('content' => NULL),
66 'template' => 'profile-wrapper',
67 ),
68 'profile_admin_overview' => array(
69 'render element' => 'form',
70 'file' => 'profile.admin.inc',
71 )
72 );
73 }
74
75 /**
76 * Implement hook_menu().
77 */
78 function profile_menu() {
79 $items['profile'] = array(
80 'title' => 'User list',
81 'page callback' => 'profile_browse',
82 'access arguments' => array('access user profiles'),
83 'file' => 'profile.pages.inc',
84 'type' => MENU_SUGGESTED_ITEM,
85 );
86 $items['admin/config/people/profile'] = array(
87 'title' => 'Profiles',
88 'description' => 'Create customizable fields for your users.',
89 'page callback' => 'drupal_get_form',
90 'page arguments' => array('profile_admin_overview'),
91 'access arguments' => array('administer users'),
92 'file' => 'profile.admin.inc',
93 );
94 $items['admin/config/people/profile/add'] = array(
95 'title' => 'Add field',
96 'page callback' => 'drupal_get_form',
97 'page arguments' => array('profile_field_form'),
98 'access arguments' => array('administer users'),
99 'file' => 'profile.admin.inc',
100 'type' => MENU_CALLBACK,
101 );
102 $items['admin/config/people/profile/autocomplete'] = array(
103 'title' => 'Profile category autocomplete',
104 'page callback' => 'profile_admin_settings_autocomplete',
105 'access arguments' => array('administer users'),
106 'file' => 'profile.admin.inc',
107 'type' => MENU_CALLBACK,
108 );
109 $items['admin/config/people/profile/edit'] = array(
110 'title' => 'Edit field',
111 'page callback' => 'drupal_get_form',
112 'page arguments' => array('profile_field_form'),
113 'access arguments' => array('administer users'),
114 'file' => 'profile.admin.inc',
115 'type' => MENU_CALLBACK,
116 );
117 $items['admin/config/people/profile/delete'] = array(
118 'title' => 'Delete field',
119 'page callback' => 'drupal_get_form',
120 'page arguments' => array('profile_field_delete'),
121 'access arguments' => array('administer users'),
122 'file' => 'profile.admin.inc',
123 'type' => MENU_CALLBACK,
124 );
125 $items['profile/autocomplete'] = array(
126 'title' => 'Profile autocomplete',
127 'page callback' => 'profile_autocomplete',
128 'access arguments' => array('access user profiles'),
129 'file' => 'profile.pages.inc',
130 'type' => MENU_CALLBACK,
131 );
132 return $items;
133 }
134
135 /**
136 * Implement hook_block_info().
137 */
138 function profile_block_info() {
139 $blocks['author-information']['info'] = t('Author information');
140 $blocks['author-information']['cache'] = DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE;
141 return $blocks;
142 }
143
144 /**
145 * Implement hook_block_configure().
146 */
147 function profile_block_configure($delta = '') {
148 // Compile a list of fields to show
149 $fields = array();
150 $result = db_query('SELECT name, title, weight, visibility FROM {profile_field} WHERE visibility IN (:visibility) ORDER BY weight', array(':visibility' => array(PROFILE_PUBLIC, PROFILE_PUBLIC_LISTINGS)));
151 foreach ($result as $record) {
152 $fields[$record->name] = check_plain($record->title);
153 }
154 $fields['user_profile'] = t('Link to full user profile');
155 $form['profile_block_author_fields'] = array(
156 '#type' => 'checkboxes',
157 '#title' => t('Profile fields to display'),
158 '#default_value' => variable_get('profile_block_author_fields', array()),
159 '#options' => $fields,
160 '#description' => t('Select which profile fields you wish to display in the block. Only fields designated as public in the <a href="@profile-admin">profile field configuration</a> are available.', array('@profile-admin' => url('admin/config/people/profile'))),
161 );
162 return $form;
163 }
164
165 /**
166 * Implement hook_block_save().
167 */
168 function profile_block_save($delta = '', $edit = array()) {
169 variable_set('profile_block_author_fields', $edit['profile_block_author_fields']);
170 }
171
172 /**
173 * Implement hook_block_view().
174 */
175 function profile_block_view($delta = '') {
176 if (user_access('access user profiles')) {
177 $output = '';
178 if ((arg(0) == 'node') && is_numeric(arg(1)) && (arg(2) == NULL)) {
179 $node = node_load(arg(1));
180 $account = user_load(array('uid' => $node->uid));
181
182 if ($use_fields = variable_get('profile_block_author_fields', array())) {
183 // Compile a list of fields to show.
184 $fields = array();
185 $result = db_query('SELECT name, title, weight, visibility FROM {profile_field} WHERE visibility IN (:visibility) ORDER BY weight', array(':visibility' => array(PROFILE_PUBLIC, PROFILE_PUBLIC_LISTINGS)));
186 foreach ($result as $record) {
187 // Ensure that field is displayed only if it is among the defined block fields and, if it is private, the user has appropriate permissions.
188 if (isset($use_fields[$record->name]) && $use_fields[$record->name]) {
189 $fields[] = $record;
190 }
191 }
192 }
193
194 if (!empty($fields)) {
195 $profile = _profile_update_user_fields($fields, $account);
196 $output .= theme('profile_block', array('account' => $account, 'fields' => $profile));
197 }
198
199 if (isset($use_fields['user_profile']) && $use_fields['user_profile']) {
200 $output .= '<div>' . l(t('View full user profile'), 'user/' . $account->uid) . '</div>';
201 }
202 }
203
204 if ($output) {
205 $block['subject'] = t('About %name', array('%name' => format_username($account)));
206 $block['content'] = $output;
207 return $block;
208 }
209 }
210 }
211
212 /**
213 * Implement hook_user_presave().
214 */
215 function profile_user_presave(&$edit, $account, $category) {
216 if ($account->uid) {
217 profile_save_profile($edit, $account, $category);
218 }
219 }
220
221 /**
222 * Implement hook_user_insert().
223 */
224 function profile_user_insert(&$edit, $account, $category) {
225 profile_save_profile($edit, $account, $category, TRUE);
226 }
227
228 /**
229 * Implement hook_user_cancel().
230 */
231 function profile_user_cancel(&$edit, $account, $method) {
232 switch ($method) {
233 case 'user_cancel_reassign':
234 case 'user_cancel_delete':
235 db_delete('profile_value')
236 ->condition('uid', $account->uid)
237 ->execute();
238 break;
239 }
240 }
241
242 /**
243 * Implement hook_user_load().
244 */
245 function profile_user_load($users) {
246 $result = db_query('SELECT f.name, f.type, v.uid, v.value FROM {profile_field} f INNER JOIN {profile_value} v ON f.fid = v.fid WHERE uid IN (:uids)', array(':uids' => array_keys($users)));
247 foreach ($result as $record) {
248 if (empty($users[$record->uid]->{$record->name})) {
249 $users[$record->uid]->{$record->name} = _profile_field_serialize($record->type) ? unserialize($record->value) : $record->value;
250 }
251 }
252 }
253
254 function profile_save_profile(&$edit, $account, $category, $register = FALSE) {
255 $result = _profile_get_fields($category, $register);
256 foreach ($result as $field) {
257 if (_profile_field_serialize($field->type)) {
258 $edit[$field->name] = serialize($edit[$field->name]);
259 }
260 db_merge('profile_value')
261 ->key(array(
262 'fid' => $field->fid,
263 'uid' => $account->uid,
264 ))
265 ->fields(array('value' => $edit[$field->name]))
266 ->execute();
267 // Mark field as handled (prevents saving to user->data).
268 $edit[$field->name] = NULL;
269 }
270 }
271
272 function profile_view_field($account, $field) {
273 // Only allow browsing of private fields for admins, if browsing is enabled,
274 // and if a user has permission to view profiles. Note that this check is
275 // necessary because a user may always see their own profile.
276 $browse = user_access('access user profiles')
277 && (user_access('administer users') || $field->visibility != PROFILE_PRIVATE)
278 && !empty($field->page);
279
280 if (isset($account->{$field->name}) && $value = $account->{$field->name}) {
281 switch ($field->type) {
282 case 'textarea':
283 return check_markup($value, filter_default_format($account), '', TRUE);
284 case 'textfield':
285 case 'selection':
286 return $browse ? l($value, 'profile/' . $field->name . '/' . $value) : check_plain($value);
287 case 'checkbox':
288 return $browse ? l($field->title, 'profile/' . $field->name) : check_plain($field->title);
289 case 'url':
290 return '<a href="' . check_url($value) . '">' . check_plain($value) . '</a>';
291 case 'date':
292 $format = substr(variable_get('date_format_short', 'm/d/Y - H:i'), 0, 5);
293 // Note: Avoid PHP's date() because it does not handle dates before
294 // 1970 on Windows. This would make the date field useless for e.g.
295 // birthdays.
296 $replace = array(
297 'd' => sprintf('%02d', $value['day']),
298 'j' => $value['day'],
299 'm' => sprintf('%02d', $value['month']),
300 'M' => map_month($value['month']),
301 'Y' => $value['year'],
302 'H:i' => NULL,
303 'g:ia' => NULL,
304 );
305 return strtr($format, $replace);
306 case 'list':
307 $values = preg_split("/[,\n\r]/", $value);
308 $fields = array();
309 foreach ($values as $value) {
310 if ($value = trim($value)) {
311 $fields[] = $browse ? l($value, 'profile/' . $field->name . '/' . $value) : check_plain($value);
312 }
313 }
314 return implode(', ', $fields);
315 }
316 }
317 }
318
319 /**
320 * Implement hook_user_view().
321 */
322 function profile_user_view($account) {
323 // Show private fields to administrators and people viewing their own account.
324 if (user_access('administer users') || $GLOBALS['user']->uid == $account->uid) {
325 $result = db_query('SELECT * FROM {profile_field} WHERE visibility <> :hidden ORDER BY category, weight', array(':hidden' => PROFILE_HIDDEN));
326 }
327 else {
328 $result = db_query('SELECT * FROM {profile_field} WHERE visibility <> :private AND visibility <> :hidden ORDER BY category, weight', array(':private' => PROFILE_PRIVATE, ':hidden' => PROFILE_HIDDEN));
329 }
330
331 $fields = array();
332 foreach ($result as $field) {
333 if ($value = profile_view_field($account, $field)) {
334 $title = ($field->type != 'checkbox') ? check_plain($field->title) : NULL;
335
336 // Create a single fieldset for each category.
337 if (!isset($account->content[$field->category])) {
338 $account->content[$field->category] = array(
339 '#type' => 'user_profile_category',
340 '#title' => $field->category,
341 );
342 }
343
344 $account->content[$field->category][$field->name] = array(
345 '#type' => 'user_profile_item',
346 '#title' => $title,
347 '#markup' => $value,
348 '#weight' => $field->weight,
349 '#attributes' => array('class' => array('profile-' . $field->name)),
350 );
351 }
352 }
353 }
354
355 function _profile_form_explanation($field) {
356 $output = $field->explanation;
357
358 if ($field->type == 'list') {
359 $output .= ' ' . t('Put each item on a separate line or separate them by commas. No HTML allowed.');
360 }
361
362 if ($field->visibility == PROFILE_PRIVATE) {
363 $output .= ' ' . t('The content of this field is kept private and will not be shown publicly.');
364 }
365
366 return $output;
367 }
368
369 /**
370 * Implement hook_form_alter().
371 */
372 function profile_form_alter(&$form, &$form_state, $form_id) {
373 if (!($form_id == 'user_register_form' || $form_id == 'user_profile_form')) {
374 return;
375 }
376 $form['#validate'][] = 'profile_user_form_validate';
377 $account = $form['#user'];
378 $result = _profile_get_fields($form['#user_category'], $form['#user_category'] == 'register');
379 $weight = 1;
380 foreach ($result as $field) {
381 $category = $field->category;
382 if (!isset($form[$category])) {
383 $form[$category] = array('#type' => 'fieldset', '#title' => check_plain($category), '#weight' => $weight++);
384 }
385 switch ($field->type) {
386 case 'textfield':
387 case 'url':
388 $form[$category][$field->name] = array(
389 '#type' => 'textfield',
390 '#title' => check_plain($field->title),
391 '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
392 '#maxlength' => 255,
393 '#description' => _profile_form_explanation($field),
394 '#required' => $field->required,
395 );
396 if ($field->autocomplete) {
397 $form[$category][$field->name]['#autocomplete_path'] = "profile/autocomplete/" . $field->fid;
398 }
399 break;
400
401 case 'textarea':
402 $form[$category][$field->name] = array(
403 '#type' => 'textarea',
404 '#title' => check_plain($field->title),
405 '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
406 '#description' => _profile_form_explanation($field),
407 '#required' => $field->required,
408 );
409 break;
410
411 case 'list':
412 $form[$category][$field->name] = array(
413 '#type' => 'textarea',
414 '#title' => check_plain($field->title),
415 '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
416 '#description' => _profile_form_explanation($field),
417 '#required' => $field->required,
418 );
419 break;
420
421 case 'checkbox':
422 $form[$category][$field->name] = array(
423 '#type' => 'checkbox',
424 '#title' => check_plain($field->title),
425 '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
426 '#description' => _profile_form_explanation($field),
427 '#required' => $field->required,
428 );
429 break;
430
431 case 'selection':
432 $options = $field->required ? array() : array('--');
433 $lines = preg_split("/[\n\r]/", $field->options);
434 foreach ($lines as $line) {
435 if ($line = trim($line)) {
436 $options[$line] = $line;
437 }
438 }
439 $form[$category][$field->name] = array(
440 '#type' => 'select',
441 '#title' => check_plain($field->title),
442 '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
443 '#options' => $options,
444 '#description' => _profile_form_explanation($field),
445 '#required' => $field->required,
446 );
447 break;
448
449 case 'date':
450 $form[$category][$field->name] = array(
451 '#type' => 'date',
452 '#title' => check_plain($field->title),
453 '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
454 '#description' => _profile_form_explanation($field),
455 '#required' => $field->required,
456 );
457 break;
458 }
459 }
460 }
461
462 /**
463 * Helper function: update an array of user fields by calling profile_view_field
464 */
465 function _profile_update_user_fields($fields, $account) {
466 foreach ($fields as $key => $field) {
467 $fields[$key]->value = profile_view_field($account, $field);
468 }
469 return $fields;
470 }
471
472 /**
473 * Form validation handler for the user register/profile form.
474 *
475 * @see profile_form_alter()
476 */
477 function profile_user_form_validate($form, &$form_state) {
478 $result = _profile_get_fields($form['#user_category'], $form['#user_category'] == 'register');
479 foreach ($result as $field) {
480 if (!empty($form_state['values'][$field->name])) {
481 if ($field->type == 'url' && !valid_url($form_state['values'][$field->name], TRUE)) {
482 form_set_error($field->name, t('The value provided for %field is not a valid URL.', array('%field' => $field->title)));
483 }
484 }
485 elseif ($field->required && !user_access('administer users')) {
486 form_set_error($field->name, t('The field %field is required.', array('%field' => $field->title)));
487 }
488 }
489 }
490
491 /**
492 * Implement hook_user_categories().
493 */
494 function profile_user_categories() {
495 $result = db_query("SELECT DISTINCT(category) FROM {profile_field}");
496 $data = array();
497 foreach ($result as $category) {
498 $data[] = array(
499 'name' => $category->category,
500 'title' => $category->category,
501 'weight' => 3,
502 'access callback' => 'profile_category_access',
503 'access arguments' => array(1, $category->category)
504 );
505 }
506 return $data;
507 }
508
509 /**
510 * Menu item access callback - check if a user has access to a profile category.
511 */
512 function profile_category_access($account, $category) {
513 if (user_access('administer users') && $account->uid > 0) {
514 return TRUE;
515 }
516 else {
517 $category_visible = (bool) db_query_range('SELECT 1 FROM {profile_field} WHERE category = :category AND visibility <> :visibility', 0, 1, array(
518 ':category' => $category,
519 ':visibility' => PROFILE_HIDDEN
520 ))->fetchField();
521 return user_edit_access($account) && $category_visible;
522 }
523 }
524
525 /**
526 * Process variables for profile-block.tpl.php.
527 *
528 * The $variables array contains the following arguments:
529 * - $account
530 * - $fields
531 *
532 * @see profile-block.tpl.php
533 */
534 function template_preprocess_profile_block(&$variables) {
535
536 $variables['user_picture'] = theme('user_picture', array('account' => $variables['account']));
537 $variables['profile'] = array();
538 // Supply filtered version of $fields that have values.
539 foreach ($variables['fields'] as $field) {
540 if ($field->value) {
541 $variables['profile'][$field->name]->title = check_plain($field->title);
542 $variables['profile'][$field->name]->value = $field->value;
543 $variables['profile'][$field->name]->type = $field->type;
544 }
545 }
546
547 }
548
549 /**
550 * Process variables for profile-listing.tpl.php.
551 *
552 * The $variables array contains the following arguments:
553 * - $account
554 * - $fields
555 *
556 * @see profile-listing.tpl.php
557 */
558 function template_preprocess_profile_listing(&$variables) {
559
560 $variables['user_picture'] = theme('user_picture', array('account' => $variables['account']));
561 $variables['name'] = theme('username', array('account' => $variables['account']));
562 $variables['profile'] = array();
563 // Supply filtered version of $fields that have values.
564 foreach ($variables['fields'] as $field) {
565 if ($field->value) {
566 $variables['profile'][$field->name]->title = $field->title;
567 $variables['profile'][$field->name]->value = $field->value;
568 $variables['profile'][$field->name]->type = $field->type;
569 }
570 }
571
572 }
573
574 /**
575 * Process variables for profile-wrapper.tpl.php.
576 *
577 * The $variables array contains the following arguments:
578 * - $content
579 *
580 * @see profile-wrapper.tpl.php
581 */
582 function template_preprocess_profile_wrapper(&$variables) {
583 $variables['current_field'] = '';
584 if ($field = arg(1)) {
585 $variables['current_field'] = $field;
586 // Supply an alternate template suggestion based on the browsable field.
587 $variables['template_files'][] = 'profile-wrapper-' . $field;
588 }
589 }
590
591 function _profile_field_types($type = NULL) {
592 $types = array('textfield' => t('single-line textfield'),
593 'textarea' => t('multi-line textfield'),
594 'checkbox' => t('checkbox'),
595 'selection' => t('list selection'),
596 'list' => t('freeform list'),
597 'url' => t('URL'),
598 'date' => t('date'));
599 return isset($type) ? $types[$type] : $types;
600 }
601
602 function _profile_field_serialize($type = NULL) {
603 return $type == 'date';
604 }
605
606 function _profile_get_fields($category, $register = FALSE) {
607 $query = db_select('profile_field');
608 if ($register) {
609 $query->condition('register', 1);
610 }
611 else {
612 // Use LOWER(:category) instead of PHP's strtolower() to avoid UTF-8 conversion issues.
613 $query->where('LOWER(category) = LOWER(:category)', array(':category' => $category));
614 }
615 if (!user_access('administer users')) {
616 $query->condition('visibility', PROFILE_HIDDEN, '<>');
617 }
618 return $query
619 ->fields('profile_field')
620 ->orderBy('category', 'ASC')
621 ->orderBy('weight', 'ASC')
622 ->execute();
623 }
624

  ViewVC Help
Powered by ViewVC 1.1.2