profile.module 26.6 KB
Newer Older
Dries's avatar
   
Dries committed
1
<?php
Dries's avatar
Dries committed
2
// $Id$
Dries's avatar
   
Dries committed
3

Dries's avatar
   
Dries committed
4
5
6
7
8
/**
 * @file
 * Support for configurable user profiles.
 */

9
10
11
12
13
14
/**
 * Flags to define the visibility of a profile field.
 */
define('PROFILE_PRIVATE', 1);
define('PROFILE_PUBLIC', 2);
define('PROFILE_PUBLIC_LISTINGS', 3);
15
define('PROFILE_HIDDEN', 4);
16

Dries's avatar
   
Dries committed
17
18
19
/**
 * Implementation of hook_help().
 */
Dries's avatar
   
Dries committed
20
function profile_help($section) {
Dries's avatar
   
Dries committed
21
  switch ($section) {
Dries's avatar
   
Dries committed
22
    case 'admin/modules#description':
23
      return t('Supports configurable user profiles.');
24
    case 'admin/settings/profile':
25
      return t('<p>Here you can define custom fields that users can fill in in their user profile (such as <em>country</em>, <em>real name</em>, <em>age</em>, ...).</p>');
Dries's avatar
   
Dries committed
26
27
28
  }
}

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
 * Implementation of hook_block().
 */
function profile_block($op = 'list', $delta = 0, $edit = array()) {

  if ($op == 'list') {
     $blocks[0]['info'] = t('Author information');

     return $blocks;
  }
  else if ($op == 'configure' && $delta == 0) {
    // Compile a list of fields to show
    $fields = array();
    $result = db_query('SELECT name, title FROM {profile_fields} ORDER BY weight');
    while ($record = db_fetch_object($result)) {
      $fields[$record->name] = $record->title;
    }
    $fields['user_profile'] = t('Link to full user profile');
47
48
    $form['profile_block_author_fields'] = array(type => 'checkboxes', title => t('Profile fields to display'), default_value => variable_get('profile_block_author_fields', NULL), options => $fields, 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/settings/profile'))));
    return $form;
49
50
51
52
53
54
55
  }
  else if ($op == 'save' && $delta == 0) {
    variable_set('profile_block_author_fields', $edit['profile_block_author_fields']);
  }
  else if ($op == 'view') {
    if (user_access('access user profiles')) {
      if ((arg(0) == 'node') && is_numeric(arg(1)) && (arg(2) == NULL)) {
56
        $node = node_load(arg(1));
57
58
59
60
61
        $account = user_load(array('uid' => $node->uid));

        if ($use_fields = variable_get('profile_block_author_fields', array())) {
          // Compile a list of fields to show
          $fields = array();
62
          $result = db_query('SELECT name, title, type, visibility FROM {profile_fields} WHERE visibility IN (%d, %d) ORDER BY weight', PROFILE_PUBLIC, PROFILE_PUBLIC_LISTINGS);
63
64
65
66
67
68
69
70
71
            while ($record = db_fetch_object($result)) {
              // Endure that field is displayed only if it is among the defined block fields and, if it is private, the user has appropriate permissions.
              if (in_array($record->name, $use_fields)) {
              $fields[] = $record;
            }
          }
        }

        if ($fields) {
72
          _profile_update_user_fields($fields, $account);
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
          $output .= theme('profile_block', $account, $fields, true);
        }

        if (in_array('user_profile', $use_fields)) {
          $output .= '<div>' . l(t('View full user profile'), 'user/' . $account->uid) . '</div>';
        }
      }

      if ($output) {
         $block['subject'] = t('About %name', array('%name' => $account->name));
         $block['content'] = $output;
         return $block;
      }
    }
  }
}

Dries's avatar
   
Dries committed
90
/**
Dries's avatar
   
Dries committed
91
 * Implementation of hook_menu().
Dries's avatar
   
Dries committed
92
 */
Dries's avatar
   
Dries committed
93
function profile_menu($may_cache) {
94
  global $user;
Dries's avatar
   
Dries committed
95
  $items = array();
Dries's avatar
   
Dries committed
96
97
98
99

  if ($may_cache) {
    $items[] = array('path' => 'profile', 'title' => t('user list'),
      'callback' => 'profile_browse',
100
      'access' => user_access('access user profiles'),
Dries's avatar
   
Dries committed
101
      'type' => MENU_SUGGESTED_ITEM);
102
    $items[] = array('path' => 'admin/settings/profile', 'title' => t('profiles'),
Dries's avatar
   
Dries committed
103
      'callback' => 'profile_admin_overview',
104
105
      'access' => user_access('administer users'));
    $items[] = array('path' => 'admin/settings/profile/add', 'title' => t('add field'),
Dries's avatar
   
Dries committed
106
107
108
      'callback' => 'profile_admin_add',
      'access' => user_access('administer users'),
      'type' => MENU_CALLBACK);
109
    $items[] = array('path' => 'admin/settings/profile/edit', 'title' => t('edit field'),
Dries's avatar
   
Dries committed
110
111
112
      'callback' => 'profile_admin_edit',
      'access' => user_access('administer users'),
      'type' => MENU_CALLBACK);
113
    $items[] = array('path' => 'admin/settings/profile/delete', 'title' => t('delete field'),
Dries's avatar
   
Dries committed
114
115
116
117
      'callback' => 'profile_admin_delete',
      'access' => user_access('administer users'),
      'type' => MENU_CALLBACK);
  }
118

Dries's avatar
   
Dries committed
119
  return $items;
Dries's avatar
   
Dries committed
120
}
Dries's avatar
   
Dries committed
121

Dries's avatar
   
Dries committed
122
123
124
/**
 * Menu callback; display a list of user information.
 */
Dries's avatar
   
Dries committed
125
function profile_browse() {
Dries's avatar
   
Dries committed
126

Steven Wittens's avatar
Steven Wittens committed
127
128
  $name = arg(1);
  $value = arg(2);
Dries's avatar
   
Dries committed
129

Steven Wittens's avatar
Steven Wittens committed
130
  $field = db_fetch_object(db_query("SELECT DISTINCT(fid), type, title, page, visibility FROM {profile_fields} WHERE name = '%s'", $name));
Dries's avatar
   
Dries committed
131

Dries's avatar
   
Dries committed
132
  if ($name && $field->fid) {
Steven Wittens's avatar
Steven Wittens committed
133
134
135
136
137
138
    // Do not allow browsing of private fields by non-admins
    if (!user_access('administer users') && $field->visibility == PROFILE_PRIVATE) {
       drupal_access_denied();
       return;
    }

139
    // Compile a list of fields to show
Dries's avatar
   
Dries committed
140
    $fields = array();
Dries's avatar
   
Dries committed
141
    $result = db_query('SELECT name, title, type FROM {profile_fields} WHERE fid != %d AND visibility = %d ORDER BY weight', $field->fid, PROFILE_PUBLIC_LISTINGS);
Dries's avatar
   
Dries committed
142
143
144
    while ($record = db_fetch_object($result)) {
      $fields[] = $record;
    }
Dries's avatar
   
Dries committed
145

Dries's avatar
Dries committed
146
147
148
149
150
151
    // Determine what query to use:
    switch ($field->type) {
      case 'checkbox':
        $query = 'v.value = 1';
        break;
      case 'selection':
Dries's avatar
   
Dries committed
152
        $query = "v.value = '". db_escape_string($value) ."'";
Dries's avatar
Dries committed
153
154
        break;
      case 'list':
Dries's avatar
   
Dries committed
155
        $query = "v.value LIKE '%%". db_escape_string($value) ."%%'";
Dries's avatar
Dries committed
156
        break;
Steven Wittens's avatar
Steven Wittens committed
157
158
159
      default:
        drupal_not_found();
        return;
Dries's avatar
Dries committed
160
161
    }

Dries's avatar
   
Dries committed
162
    // Extract the affected users:
Dries's avatar
Dries committed
163
    $result = pager_query("SELECT u.uid FROM {users} u INNER JOIN {profile_values} v ON u.uid = v.uid WHERE v.fid = %d AND $query ORDER BY u.access DESC", 20, 0, NULL, $field->fid);
Dries's avatar
   
Dries committed
164

Dries's avatar
   
Dries committed
165
    $output = '<div id="profile">';
Dries's avatar
   
Dries committed
166
    while ($account = db_fetch_object($result)) {
167
168
169
      $account = user_load(array('uid' => $account->uid));
      _profile_update_user_fields($fields, $account);
      $output .= theme('profile_listing', $account, $fields);
Dries's avatar
   
Dries committed
170
171
172
    }
    $output .= theme('pager', NULL, 20);

Dries's avatar
Dries committed
173
    if ($field->type == 'selection' || $field->type == 'list') {
174
      $title = strtr($field->page, array('%value' => theme('placeholder', $value)));
Dries's avatar
   
Dries committed
175
176
    }
    else {
Dries's avatar
   
Dries committed
177
      $title = $field->page;
Dries's avatar
   
Dries committed
178
    }
Dries's avatar
   
Dries committed
179
    $output .= '</div>';
Dries's avatar
   
Dries committed
180

181
    drupal_set_title($title);
Dries's avatar
   
Dries committed
182
    return $output;
Dries's avatar
   
Dries committed
183
  }
Dries's avatar
   
Dries committed
184
  else if ($name && !$field->id) {
Dries's avatar
   
Dries committed
185
    drupal_not_found();
Dries's avatar
   
Dries committed
186
  }
Dries's avatar
   
Dries committed
187
188
189
190
191
192
193
194
195
  else {
    // Compile a list of fields to show
    $fields = array();
    $result = db_query('SELECT name, title, type FROM {profile_fields} WHERE visibility = %d', PROFILE_PUBLIC_LISTINGS);
    while ($record = db_fetch_object($result)) {
      $fields[] = $record;
    }

    // Extract the affected users:
Dries's avatar
Dries committed
196
    $result = pager_query("SELECT uid FROM {users} WHERE uid > 0 ORDER BY access DESC", 20, 0, NULL);
Dries's avatar
   
Dries committed
197
198
199

    $output = '<div id="profile">';
    while ($account = db_fetch_object($result)) {
200
201
202
      $account = user_load(array('uid' => $account->uid));
      _profile_update_user_fields($fields, $account);
      $output .= theme('profile_listing', $account, $fields);
Dries's avatar
   
Dries committed
203
204
205
206
    }
    $output .= '</div>';
    $output .= theme('pager', NULL, 20);

207
    drupal_set_title(t('user list'));
Dries's avatar
   
Dries committed
208
    return $output;
Dries's avatar
   
Dries committed
209
  }
Dries's avatar
   
Dries committed
210
}
Dries's avatar
   
Dries committed
211

Dries's avatar
   
Dries committed
212
function profile_load_profile(&$user) {
Steven Wittens's avatar
Steven Wittens committed
213
  $result = db_query('SELECT f.name, f.type, v.value FROM {profile_fields} f INNER JOIN {profile_values} v ON f.fid = v.fid WHERE uid = %d', $user->uid);
Dries's avatar
   
Dries committed
214
215
  while ($field = db_fetch_object($result)) {
    if (empty($user->{$field->name})) {
Steven Wittens's avatar
Steven Wittens committed
216
      $user->{$field->name} = _profile_field_serialize($field->type) ? unserialize($field->value) : $field->value;
Dries's avatar
   
Dries committed
217
    }
Dries's avatar
   
Dries committed
218
  }
Dries's avatar
   
Dries committed
219
220
}

221
function profile_save_profile(&$edit, &$user, $category) {
222
  if ($_GET['q'] == 'user/register' || $_GET['q'] == 'admin/user/create') {
223
    $result = db_query('SELECT fid, name, type FROM {profile_fields} WHERE register = 1 AND visibility != %d ORDER BY category, weight', PROFILE_HIDDEN);
Dries's avatar
   
Dries committed
224
225
  }
  else {
226
    $result = db_query("SELECT fid, name, type FROM {profile_fields} WHERE LOWER(category) = LOWER('%s') AND visibility != %d", $category, PROFILE_HIDDEN);
227
    // We use LOWER('%s') instead of PHP's strtolower() to avoid UTF-8 conversion issues.
Dries's avatar
   
Dries committed
228
  }
Dries's avatar
   
Dries committed
229
  while ($field = db_fetch_object($result)) {
Steven Wittens's avatar
Steven Wittens committed
230
231
232
    if (_profile_field_serialize($field->type)) {
       $edit[$field->name] = serialize($edit[$field->name]);
    }
233
234
    db_query("DELETE FROM {profile_values} WHERE fid = %d AND uid = %d", $field->fid, $user->uid);
    db_query("INSERT INTO {profile_values} (fid, uid, value) VALUES (%d, %d, '%s')", $field->fid, $user->uid, $edit[$field->name]);
235
    // Mark field as handled (prevents saving to user->data).
236
    $edit[$field->name] = NULL;
Dries's avatar
   
Dries committed
237
  }
Dries's avatar
   
Dries committed
238
239
}

240
function profile_view_field($user, $field) {
Steven Wittens's avatar
Steven Wittens committed
241
242
243
  // Only allow browsing of private fields for admins
  $browse = user_access('administer users') || $field->visibility != PROFILE_PRIVATE;

Dries's avatar
Dries committed
244
245
246
  if ($field->fid == 18 || $field->fid == 19 || $field->fid == 20) {
    return;
  }
Dries's avatar
Dries committed
247

248
249
250
  if ($value = $user->{$field->name}) {
    switch ($field->type) {
      case 'textfield':
251
        return check_plain($value);
252
      case 'textarea':
253
        return check_markup($value);
254
      case 'selection':
255
        return $browse ? l($value, "profile/$field->name/$value") : check_plain($value);
256
      case 'checkbox':
257
        return $browse ? l($field->title, "profile/$field->name") : check_plain($field->title);
258
      case 'url':
259
        return '<a href="'. check_url($value) .'">'. check_plain($value) .'</a>';
Steven Wittens's avatar
Steven Wittens committed
260
261
262
263
264
265
266
267
268
269
270
      case 'date':
        list($format) = explode(' - ', variable_get('date_format_short', 'm/d/Y - H:i'), 2);
        // Note: we avoid PHP's date() because it does not handle dates before
        // 1970 on Windows. This would make the date field useless for e.g.
        // birthdays.
        $replace = array('d' => sprintf('%02d', $value['day']),
                         'j' => $value['day'],
                         'm' => sprintf('%02d', $value['month']),
                         'M' => _profile_map_month($value['month']),
                         'Y' => $value['year']);
        return strtr($format, $replace);
271
      case 'list':
Dries's avatar
   
Dries committed
272
        $values = split("[,\n\r]", $value);
Dries's avatar
Dries committed
273
274
        $fields = array();
        foreach ($values as $value) {
Steven Wittens's avatar
Steven Wittens committed
275
          if ($value = trim($value)) {
276
            $fields[] = $browse ? l($value, "profile/". urlencode($field->name) ."/". urlencode($value)) : check_plain($value);
Dries's avatar
Dries committed
277
278
279
          }
        }
        return implode(', ', $fields);
280
281
282
283
    }
  }
}

Dries's avatar
   
Dries committed
284
function profile_view_profile($user) {
Dries's avatar
   
Dries committed
285

286
  profile_load_profile($user);
Dries's avatar
   
Dries committed
287

Steven Wittens's avatar
Steven Wittens committed
288
289
  // Show private fields to administrators and people viewing their own account.
  if (user_access('administer users') || $GLOBALS['user']->uid == $user->uid) {
290
    $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d ORDER BY category, weight', PROFILE_HIDDEN);
Steven Wittens's avatar
Steven Wittens committed
291
292
  }
  else {
293
    $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d AND visibility != %d ORDER BY category, weight', PROFILE_PRIVATE, PROFILE_HIDDEN);
Steven Wittens's avatar
Steven Wittens committed
294
295
  }

Dries's avatar
   
Dries committed
296
  while ($field = db_fetch_object($result)) {
297
    if ($value = profile_view_field($user, $field)) {
Steven Wittens's avatar
Steven Wittens committed
298
      $description = ($field->visibility == PROFILE_PRIVATE) ? t('The content of this field is private and only visible to yourself.') : '';
299
      $title = ($field->type != 'checkbox') ? check_plain($field->title) : '';
300
301
      $form = array(type => 'item', title => $title, value => $value, description => $description);
      $fields[$field->category][$field->name] = form_render(_form_builder($form));
Dries's avatar
   
Dries committed
302
303
304
    }
  }

Dries's avatar
Dries committed
305
  return $fields;
Dries's avatar
   
Dries committed
306
}
Dries's avatar
   
Dries committed
307

308
309
function _profile_form_explanation($field) {
  $output = $field->explanation;
Dries's avatar
   
Dries committed
310

311
  if ($field->type == 'list') {
Dries's avatar
   
Dries committed
312
    $output .= ' '. t('Put each item on a separate line or separate them by commas.  No HTML allowed.');
313
314
315
316
317
318
319
320
321
322
323
  }

  if ($field->visibility == PROFILE_PRIVATE) {
    $output .= ' '. t('The content of this field is kept private and will not be shown publicly.');
  }

  return $output;
}

function profile_form_profile($edit, $user, $category) {

324
  if ($_GET['q'] == 'user/register' || $_GET['q'] == 'admin/user/create') {
Dries's avatar
   
Dries committed
325
326
327
    $result = db_query('SELECT * FROM {profile_fields} WHERE register = 1 ORDER BY category, weight');
  }
  else {
328
329
    $result = db_query("SELECT * FROM {profile_fields} WHERE LOWER(category) = LOWER('%s') ORDER BY weight", $category);
    // We use LOWER('%s') instead of PHP's strtolower() to avoid UTF-8 conversion issues.
Dries's avatar
   
Dries committed
330
  }
331
332
333
334
  // Only add form group if items exist
  if (db_num_rows($result)) {
    $fields[$category] = array(type => 'fieldset', title => $category);
  }
Dries's avatar
   
Dries committed
335
  while ($field = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
336
    $category = $field->category;
Dries's avatar
   
Dries committed
337
338
    switch ($field->type) {
      case 'textfield':
339
340
      case 'url':        
        $fields[$category][$field->name] = array(type => 'textfield', title => check_plain($field->title), default_value => $edit[$field->name], size => 60, maxlength => 255, description => _profile_form_explanation($field), required => $field->required);
Dries's avatar
   
Dries committed
341
        break;
342
343
      case 'textarea':       
        $fields[$category][$field->name] = array(type => 'textarea', title => check_plain($field->title), default_value => $edit[$field->name], cols => 60, rows => 5, description => _profile_form_explanation($field), required => $field->required);
Dries's avatar
Dries committed
344
345
        break;
      case 'list':
346
        $fields[$category][$field->name] = array(type => 'textarea', title => check_plain($field->title), default_value => $edit[$field->name], cols => 60, rows => 5, description => _profile_form_explanation($field), required => $field->required);
Dries's avatar
   
Dries committed
347
        break;
348
349
      case 'checkbox':        
        $fields[$category][$field->name] = array(type => 'checkbox', title => check_plain($field->title), return_value => 1, default_value => $edit[$field->name], description => _profile_form_explanation($field), required => $field->required);
Dries's avatar
   
Dries committed
350
351
352
        break;
      case 'selection':
        $options = array('--');
Dries's avatar
   
Dries committed
353
        $lines = split("[,\n\r]", $field->options);
Dries's avatar
   
Dries committed
354
355
356
357
358
        foreach ($lines as $line) {
          if ($line = trim($line)) {
            $options[$line] = $line;
          }
        }
359
        $fields[$category][$field->name] = array(type => 'select', title => check_plain($field->title), default_value => $edit[$field->name], options => $options, description => _profile_form_explanation($field), required => $field->required);
Dries's avatar
   
Dries committed
360
        break;
Steven Wittens's avatar
Steven Wittens committed
361
      case 'date':
362
        $fields[$category][$field->name] = array(type => 'date', title => check_plain($field->title), default_value => $edit[$field->name], description, description => _profile_form_explanation($field), required => $field->required);
Steven Wittens's avatar
Steven Wittens committed
363
        break;
Dries's avatar
   
Dries committed
364
365
    }
  }
366
  return $fields;
Dries's avatar
   
Dries committed
367
368
}

369
370
371
372
373
374
375
376
377
378
379
/**
 * Helper function: update an array of user fields by calling profile_view_field
 */
function _profile_update_user_fields(&$fields, $account) {
  foreach ($fields as $key => $field) {
    if ($value = profile_view_field($account, $field)) {
      $fields[$key]->value = $value;
    }
  }
}

Steven Wittens's avatar
Steven Wittens committed
380
381
382
383
384
385
386
387

/**
 * Helper function for usage with drupal_map_assoc
 */
function _profile_map_month($month) {
  return format_date(gmmktime(0, 0, 0, $month, 2, 1970), 'custom', 'M', 0);
}

388
function profile_validate_profile($edit, $category) {
Dries's avatar
   
Dries committed
389

390
  if ($_GET['q'] == 'user/register' || $_GET['q'] == 'admin/user/create') {
Dries's avatar
   
Dries committed
391
392
393
    $result = db_query('SELECT * FROM {profile_fields} WHERE register = 1 ORDER BY category, weight');
  }
  else {
394
395
    $result = db_query("SELECT * FROM {profile_fields} WHERE LOWER(category) = LOWER('%s') ORDER BY weight", $category);
    // We use LOWER('%s') instead of PHP's strtolower() to avoid UTF-8 conversion issues.
Dries's avatar
   
Dries committed
396
  }
397
398

  while ($field = db_fetch_object($result)) {
399
    if ($edit[$field->name]) {
Dries's avatar
   
Dries committed
400
401
      if ($field->type == 'url') {
        if (!valid_url($edit[$field->name], true)) {
402
403
          form_set_error($field->name, t('The value provided for %field is not a valid URL.', array('%field' => theme('placeholder', $field->title))));
        }
404
405
      }
    }
406
    else if ($field->required && !user_access('administer users')) {
407
      form_set_error($field->name, t('The field %field is required.', array('%field' => theme('placeholder', $field->title))));
Dries's avatar
   
Dries committed
408
    }
409
410
411
412
413
  }

  return $edit;
}

414
415
416
function profile_categories() {
  $result = db_query("SELECT DISTINCT(category) FROM {profile_fields}");
  while ($category = db_fetch_object($result)) {
417
    $data[] = array('name' => check_plain($category->category), 'title' => $category->category, 'weight' => 3);
418
419
420
421
  }
  return $data;
}

Dries's avatar
   
Dries committed
422
423
424
/**
 * Implementation of hook_user().
 */
425
function profile_user($type, &$edit, &$user, $category = NULL) {
Dries's avatar
   
Dries committed
426
427
428
  switch ($type) {
    case 'load':
      return profile_load_profile($user);
Dries's avatar
   
Dries committed
429
430
    case 'register':
      return profile_form_profile($edit, $user, $category);
Dries's avatar
   
Dries committed
431
    case 'update':
432
    case 'insert':
433
      return profile_save_profile($edit, $user, $category);
Dries's avatar
   
Dries committed
434
435
    case 'view':
      return profile_view_profile($user);
436
    case 'form':
437
      return profile_form_profile($edit, $user, $category);
Dries's avatar
   
Dries committed
438
    case 'validate':
439
440
441
      return profile_validate_profile($edit, $category);
    case 'categories':
      return profile_categories();
Dries's avatar
   
Dries committed
442
443
  }
}
Dries's avatar
   
Dries committed
444

Dries's avatar
   
Dries committed
445
function profile_validate_form($edit) {
Dries's avatar
   
Dries committed
446

Dries's avatar
   
Dries committed
447
  // Validate the title:
Dries's avatar
   
Dries committed
448

Dries's avatar
   
Dries committed
449
  if (!$edit['title']) {
Dries's avatar
   
Dries committed
450
    form_set_error('title', t('You must enter a title.'));
Dries's avatar
   
Dries committed
451
452
  }

Dries's avatar
   
Dries committed
453
  // Validate the 'form name':
Dries's avatar
   
Dries committed
454

Dries's avatar
   
Dries committed
455
  if (eregi('[^a-z0-9_-]', $edit['name'])) {
Dries's avatar
   
Dries committed
456
    form_set_error('name', t('The specified form name contains one or more illegal characters.  Spaces or any other special characters expect dash (-) and underscore (_) are not allowed.'));
Dries's avatar
   
Dries committed
457
458
  }

Dries's avatar
   
Dries committed
459
  if (in_array($edit['name'], user_fields())) {
Dries's avatar
   
Dries committed
460
    form_set_error('name', t('The specified form name is reserved for use by Drupal.'));
Dries's avatar
   
Dries committed
461
462
  }

Dries's avatar
   
Dries committed
463
464
  // Validate the category:
  if (!$edit['category']) {
Dries's avatar
   
Dries committed
465
    form_set_error('category', t('You must enter a category.'));
Dries's avatar
   
Dries committed
466
  }
Dries's avatar
   
Dries committed
467
468
}

Dries's avatar
   
Dries committed
469
470
471
/**
 * Menu callback; adds a new field to all user profiles.
 */
Dries's avatar
   
Dries committed
472
473
474
function profile_admin_add($type) {
  if ($_POST['op']) {
    $data = $_POST['edit'];
Dries's avatar
   
Dries committed
475

Dries's avatar
   
Dries committed
476
477
478
    // Validate the form:
    profile_validate_form($data);

479
    if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE title = '%s' AND category = '%s'", $data['title'], $data['category']))) {
Steven Wittens's avatar
Steven Wittens committed
480
      form_set_error('title', t('The specified title is already in use.'));
481
    }
Dries's avatar
   
Dries committed
482
483

    if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE name = '%s'", $data['name']))) {
Steven Wittens's avatar
Steven Wittens committed
484
      form_set_error('name', t('The specified name is already in use.'));
485
    }
Dries's avatar
   
Dries committed
486

Dries's avatar
   
Dries committed
487
    if (!form_get_errors()) {
Dries's avatar
   
Dries committed
488
      db_query("INSERT INTO {profile_fields} (title, name, explanation, category, type, weight, required, register, visibility, options, page) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s')", $data['title'], $data['name'], $data['explanation'], $data['category'], $type, $data['weight'], $data['required'], $data['register'], $data['visibility'], $data['options'], $data['page']);
Dries's avatar
   
Dries committed
489

Dries's avatar
   
Dries committed
490
491
      cache_clear_all();

Steven Wittens's avatar
Steven Wittens committed
492
      drupal_set_message(t('The field has been created.'));
493
      drupal_goto('admin/settings/profile');
Dries's avatar
   
Dries committed
494
    }
Dries's avatar
   
Dries committed
495
  }
Dries's avatar
   
Dries committed
496
497
498
499
  else {
    $data = array('name' => 'profile_');
  }

500
  drupal_set_title(t('Add new %type', array('%type' => _profile_field_types($type))));
Dries's avatar
   
Dries committed
501
  return _profile_field_form($type, $data);
Dries's avatar
   
Dries committed
502
503
}

Dries's avatar
   
Dries committed
504
505
506
/**
 * Menu callback; displays the profile field editing form.
 */
Dries's avatar
   
Dries committed
507
function profile_admin_edit($fid) {
Dries's avatar
   
Dries committed
508

Dries's avatar
   
Dries committed
509
510
  if ($_POST['op']) {
    $data = $_POST['edit'];
Dries's avatar
   
Dries committed
511

Dries's avatar
   
Dries committed
512
513
    // Validate form:
    profile_validate_form($data);
Dries's avatar
   
Dries committed
514

Dries's avatar
   
Dries committed
515
    if (!form_get_errors()) {
Dries's avatar
   
Dries committed
516
      db_query("UPDATE {profile_fields} SET title = '%s', name = '%s', explanation = '%s', category = '%s', weight = %d, required = %d, register = %d, visibility = %d, options = '%s', page = '%s' WHERE fid = %d", $data['title'], $data['name'], $data['explanation'], $data['category'], $data['weight'], $data['required'], $data['register'], $data['visibility'], $data['options'], $data['page'], $fid);
Dries's avatar
   
Dries committed
517

Dries's avatar
   
Dries committed
518
519
      cache_clear_all();

Steven Wittens's avatar
Steven Wittens committed
520
      drupal_set_message(t('The field has been updated.'));
521
      drupal_goto('admin/settings/profile');
Dries's avatar
   
Dries committed
522
    }
Dries's avatar
   
Dries committed
523
524
  }
  else {
Dries's avatar
   
Dries committed
525
    $data = db_fetch_array(db_query('SELECT * FROM {profile_fields} WHERE fid = %d', $fid));
Dries's avatar
   
Dries committed
526
527
  }

528
  drupal_set_title(t('Edit %type', array('%type' => $data['type'])));
Dries's avatar
   
Dries committed
529
  return _profile_field_form($data['type'], $data);
Dries's avatar
   
Dries committed
530
531
}

Dries's avatar
   
Dries committed
532
533
534
/**
 * Menu callback; deletes a field from all user profiles.
 */
Dries's avatar
   
Dries committed
535
function profile_admin_delete($fid) {
536
537
538
539
540
541
542
543
  $field = db_fetch_object(db_query("SELECT title FROM {profile_fields} WHERE fid = %d", $fid));
  if ($_POST['edit']['confirm']) {
    db_query('DELETE FROM {profile_fields} WHERE fid = %d', $fid);
    cache_clear_all();
    drupal_set_message(t('The field %field has been deleted.', array('%field' => theme('placeholder', $field->title))));
    drupal_goto('admin/settings/profile');
  }
  else {
544
    return confirm_form('profile_confirm_delete', $form, t('Do you want to remove the field %field?',
545
                    array('%field' => $field->title)),
546
                  'admin/settings/profile', '', t('Delete'), t('Cancel'));
547
  }
Dries's avatar
   
Dries committed
548
549
}

Dries's avatar
   
Dries committed
550
function _profile_field_form($type, $edit = array()) {
551
552
553
554
555
  
  $form['fields'] = array(type => 'fieldset', title => t('Field settings'));
  $form['fields']['category'] = array(type => 'textfield', title => t('Category'), default_value => $edit['category'], size => 60, maxlength => 128, description => t('The category the new field should be part of.  Categories are used to group fields logically.  An example category is "Personal information".'));
  $form['fields']['title'] = array(type => 'textfield', title => t('Title'), default_value => $edit['title'], size => 60, maxlength => 128, description => t('The title of the new field.  The title will be shown to the user.  An example title is "Favorite color".'));
  $form['fields']['name'] = array(type => 'textfield', title => t('Form name'), default_value => $edit['name'], size => 60, maxlength => 128, description => t('The name of the field.  The form name is not shown to the user but used internally in the HTML code and URLs.
Dries's avatar
   
Dries committed
556
Unless you know what you are doing, it is highly recommended that you prefix the form name with <code>profile_</code> to avoid name clashes with other fields.  Spaces or any other special characters except dash (-) and underscore (_) are not allowed. An example name is "profile_favorite_color" or perhaps just "profile_color".'));
557
  $form['fields']['explanation'] = array(type => 'textarea', title => t('Explanation'), default_value => $edit['explanation'], cols => 60, rows => 5, description => t('An optional explanation to go with the new field.  The explanation will be shown to the user.'));
Dries's avatar
   
Dries committed
558
  if ($type == 'selection') {
559
    $form['fields']['options'] = array(type => 'textarea', title => t('Selection options'), default_value => $edit['options'], cols => 60, rows => 5, description => t('A list of all options.  Put each option on a separate line.  Example options are "red", "blue", "green", etc.'));
Dries's avatar
   
Dries committed
560
  }
561
562
  $form['fields']['weight'] = array(type => 'weight', title => t('Weight'), default_value => $edit['weight'], delta => 5, description =>  t('The weights define the order in which the form fields are shown.  Lighter fields "float up" towards the top of the category.'));
  $form['fields']['visibility'] = array(type => 'radios', title => t('Visibility'), default_value => $edit['visibility'], options => array(PROFILE_HIDDEN => t('Hidden profile field, only accessible by administrators, modules and themes.'), PROFILE_PRIVATE => t('Private field, content only available to privileged users.'), PROFILE_PUBLIC => t('Public field, content shown on profile page but not used on member list pages.'), PROFILE_PUBLIC_LISTINGS => t('Public field, content shown on profile page and on member list pages.')));
Dries's avatar
Dries committed
563
  if ($type == 'selection' || $type == 'list') {
564
    $form['fields']['page'] = array(type => 'textfield', title => t('Page title'), default_value => $edit['page'], size => 60, maxlength => 128, description => t('The title of the page showing all users with the specified field.  The word <code>%value</code> will be substituted with the corresponding value.  An example page title is "People whose favorite color is %value".  Only applicable if the field is configured to be shown on member list pages.'));
Dries's avatar
   
Dries committed
565
566
  }
  else {
567
    $form['fields']['page'] = array(type => 'textfield', title => t('Page title'), default_value => $edit['page'], size => 60, maxlength => 128, description => t('The title of the page showing all users with the specified field.  Only applicable if the field is configured to be shown on member listings.'));
Dries's avatar
   
Dries committed
568
  }
569
570
571
  $form['fields']['required'] = array(type => 'checkbox', title => t('The user must enter a value.'), return_value => 1, default_value => $edit['required']);
  $form['fields']['register'] = array(type => 'checkbox', title => t('Visible in user registration form.'), return_value => 1, default_value => $edit['register']);
  $form['submit'] = array(type => 'submit', value => t('Save field'));
Dries's avatar
   
Dries committed
572

573
  return drupal_get_form('_profile_field_form', $form);
Dries's avatar
   
Dries committed
574
575
}

Dries's avatar
   
Dries committed
576
577
578
/**
 * Menu callback; display a listing of all editable profile fields.
 */
Dries's avatar
   
Dries committed
579
580
581
function profile_admin_overview() {

  $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight');
Steven Wittens's avatar
Steven Wittens committed
582
  $rows = array();
Dries's avatar
   
Dries committed
583
  while ($field = db_fetch_object($result)) {
584
    $rows[] = array(check_plain($field->title), $field->name, _profile_field_types($field->type), $field->category, l(t('edit'), "admin/settings/profile/edit/$field->fid"), l(t('delete'), "admin/settings/profile/delete/$field->fid"));
Steven Wittens's avatar
Steven Wittens committed
585
586
587
  }
  if (count($rows) == 0) {
    $rows[] = array(array('data' => t('No fields defined.'), 'colspan' => '6'));
Dries's avatar
   
Dries committed
588
  }
Dries's avatar
   
Dries committed
589

Dries's avatar
   
Dries committed
590
  $header = array(t('Title'), t('Name'), t('Type'), t('Category'), array('data' => t('Operations'), 'colspan' => '2'));
Dries's avatar
   
Dries committed
591
592

  $output  = theme('table', $header, $rows);
Steven Wittens's avatar
Steven Wittens committed
593
  $output .= '<h2>'. t('Add new field') .'</h2>';
Dries's avatar
   
Dries committed
594
595
  $output .= '<ul>';
  foreach (_profile_field_types() as $key => $value) {
596
    $output .= '<li>'. l($value, "admin/settings/profile/add/$key") .'</li>';
Dries's avatar
   
Dries committed
597
  }
Dries's avatar
   
Dries committed
598
599
  $output .= '</ul>';

Dries's avatar
   
Dries committed
600
  return $output;
Dries's avatar
   
Dries committed
601
602
}

603
function theme_profile_block($account, $fields = array()) {
604

605
  $output .= theme('user_picture', $account);
606
607

  foreach ($fields as $field) {
608
    if ($field->value) {
609
      $output .= "<p><strong>$field->title:</strong><br />$field->value</p>\n";
610
611
612
613
614
615
    }
  }

  return $output;
}

616
function theme_profile_listing($account, $fields = array()) {
Dries's avatar
   
Dries committed
617
618

  $output  = "<div class=\"profile\">\n";
619
620
  $output .= theme('user_picture', $account);
  $output .= ' <div class="name">'. theme('username', $account) ."</div>\n";
Dries's avatar
   
Dries committed
621
622

  foreach ($fields as $field) {
623
    if ($field->value) {
624
      $output .= " <div class=\"field\">$value</div>\n";
Dries's avatar
   
Dries committed
625
626
    }
  }
Dries's avatar
   
Dries committed
627
628
629
630
631
632
633

  $output .= "</div>\n";

  return $output;
}

function _profile_field_types($type = NULL) {
Steven Wittens's avatar
Steven Wittens committed
634
635
636
637
638
639
640
  $types = array('textfield' => t('single-line textfield'),
                 'textarea' => t('multi-line textfield'),
                 'checkbox' => t('checkbox'),
                 'selection' => t('list selection'),
                 'list' => t('freeform list'),
                 'url' => t('URL'),
                 'date' => t('date'));
Dries's avatar
   
Dries committed
641
  return isset($type) ? $types[$type] : $types;
Dries's avatar
   
Dries committed
642
643
}

Steven Wittens's avatar
Steven Wittens committed
644
645
646
647
function _profile_field_serialize($type = NULL) {
  return $type == 'date';
}

648