profile.module 27.5 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) {
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    case 'admin/help#profile':
      $output = '<p>'. t('The profile module allows you to define custom fields (such as country, real name, age, ...) in the user profile.  This permits users of a site to share more information about themselves, and can help community-based sites to organize users around profile fields.') .'</p>';
      $output .= t('<p>The following types of fields can be added to the user profile:</p>
<ul>
<li>single-line textfield</li>
<li>multi-line textfield</li>
<li>checkbox</li>
<li>list selection</li>
<li>freeform list</li>
<li>URL</li>
<li>date</li>
</ul>
');
      $output .= t('<p>You can</p>
<ul>
<li>view user <a href="%profile">profiles</a>.</li>
<li>administer profile settings: <a href="%admin-settings-profile">administer &gt;&gt; settings &gt;&gt; profiles</a>.</li>
</ul>
', array('%profile' => url('profile'), '%admin-settings-profile' => url('admin/settings/profile')));
      $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%profile">Profile page</a>.', array('%profile' => 'http://www.drupal.org/handbook/modules/profile/')) .'</p>';
      return $output;
Dries's avatar
   
Dries committed
43
    case 'admin/modules#description':
44
      return t('Supports configurable user profiles.');
45
    case 'admin/settings/profile':
46
      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
47
48
49
  }
}

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
 * 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');
68
    $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'))));
69
    return $form;
70
71
72
73
74
75
76
  }
  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)) {
77
        $node = node_load(arg(1));
78
79
80
81
82
        $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();
83
          $result = db_query('SELECT name, title, type, visibility FROM {profile_fields} WHERE visibility IN (%d, %d) ORDER BY weight', PROFILE_PUBLIC, PROFILE_PUBLIC_LISTINGS);
84
85
86
87
88
89
90
91
92
            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) {
93
          _profile_update_user_fields($fields, $account);
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
          $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
111
/**
Dries's avatar
   
Dries committed
112
 * Implementation of hook_menu().
Dries's avatar
   
Dries committed
113
 */
Dries's avatar
   
Dries committed
114
function profile_menu($may_cache) {
115
  global $user;
Dries's avatar
   
Dries committed
116
  $items = array();
Dries's avatar
   
Dries committed
117
118
119
120

  if ($may_cache) {
    $items[] = array('path' => 'profile', 'title' => t('user list'),
      'callback' => 'profile_browse',
121
      'access' => user_access('access user profiles'),
Dries's avatar
   
Dries committed
122
      'type' => MENU_SUGGESTED_ITEM);
123
    $items[] = array('path' => 'admin/settings/profile', 'title' => t('profiles'),
Dries's avatar
   
Dries committed
124
      'callback' => 'profile_admin_overview',
125
126
      'access' => user_access('administer users'));
    $items[] = array('path' => 'admin/settings/profile/add', 'title' => t('add field'),
Dries's avatar
   
Dries committed
127
128
129
      'callback' => 'profile_admin_add',
      'access' => user_access('administer users'),
      'type' => MENU_CALLBACK);
130
    $items[] = array('path' => 'admin/settings/profile/edit', 'title' => t('edit field'),
Dries's avatar
   
Dries committed
131
132
133
      'callback' => 'profile_admin_edit',
      'access' => user_access('administer users'),
      'type' => MENU_CALLBACK);
134
    $items[] = array('path' => 'admin/settings/profile/delete', 'title' => t('delete field'),
Dries's avatar
   
Dries committed
135
136
137
138
      'callback' => 'profile_admin_delete',
      'access' => user_access('administer users'),
      'type' => MENU_CALLBACK);
  }
139

Dries's avatar
   
Dries committed
140
  return $items;
Dries's avatar
   
Dries committed
141
}
Dries's avatar
   
Dries committed
142

Dries's avatar
   
Dries committed
143
144
145
/**
 * Menu callback; display a list of user information.
 */
Dries's avatar
   
Dries committed
146
function profile_browse() {
Dries's avatar
   
Dries committed
147

Steven Wittens's avatar
Steven Wittens committed
148
149
  $name = arg(1);
  $value = arg(2);
Dries's avatar
   
Dries committed
150

Steven Wittens's avatar
Steven Wittens committed
151
  $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
152

Dries's avatar
   
Dries committed
153
  if ($name && $field->fid) {
Steven Wittens's avatar
Steven Wittens committed
154
155
156
157
158
159
    // Do not allow browsing of private fields by non-admins
    if (!user_access('administer users') && $field->visibility == PROFILE_PRIVATE) {
       drupal_access_denied();
       return;
    }

160
    // Compile a list of fields to show
Dries's avatar
   
Dries committed
161
    $fields = array();
Dries's avatar
   
Dries committed
162
    $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
163
164
165
    while ($record = db_fetch_object($result)) {
      $fields[] = $record;
    }
Dries's avatar
   
Dries committed
166

Dries's avatar
Dries committed
167
168
169
170
171
172
    // Determine what query to use:
    switch ($field->type) {
      case 'checkbox':
        $query = 'v.value = 1';
        break;
      case 'selection':
Dries's avatar
   
Dries committed
173
        $query = "v.value = '". db_escape_string($value) ."'";
Dries's avatar
Dries committed
174
175
        break;
      case 'list':
Dries's avatar
   
Dries committed
176
        $query = "v.value LIKE '%%". db_escape_string($value) ."%%'";
Dries's avatar
Dries committed
177
        break;
Steven Wittens's avatar
Steven Wittens committed
178
179
180
      default:
        drupal_not_found();
        return;
Dries's avatar
Dries committed
181
182
    }

Dries's avatar
   
Dries committed
183
    // Extract the affected users:
Dries's avatar
Dries committed
184
    $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
185

Dries's avatar
   
Dries committed
186
    $output = '<div id="profile">';
Dries's avatar
   
Dries committed
187
    while ($account = db_fetch_object($result)) {
188
189
190
      $account = user_load(array('uid' => $account->uid));
      _profile_update_user_fields($fields, $account);
      $output .= theme('profile_listing', $account, $fields);
Dries's avatar
   
Dries committed
191
192
193
    }
    $output .= theme('pager', NULL, 20);

Dries's avatar
Dries committed
194
    if ($field->type == 'selection' || $field->type == 'list') {
195
      $title = strtr($field->page, array('%value' => theme('placeholder', $value)));
Dries's avatar
   
Dries committed
196
197
    }
    else {
Dries's avatar
   
Dries committed
198
      $title = $field->page;
Dries's avatar
   
Dries committed
199
    }
Dries's avatar
   
Dries committed
200
    $output .= '</div>';
Dries's avatar
   
Dries committed
201

202
    drupal_set_title($title);
Dries's avatar
   
Dries committed
203
    return $output;
Dries's avatar
   
Dries committed
204
  }
Dries's avatar
   
Dries committed
205
  else if ($name && !$field->id) {
Dries's avatar
   
Dries committed
206
    drupal_not_found();
Dries's avatar
   
Dries committed
207
  }
Dries's avatar
   
Dries committed
208
209
210
211
212
213
214
215
216
  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
217
    $result = pager_query("SELECT uid FROM {users} WHERE uid > 0 ORDER BY access DESC", 20, 0, NULL);
Dries's avatar
   
Dries committed
218
219
220

    $output = '<div id="profile">';
    while ($account = db_fetch_object($result)) {
221
222
223
      $account = user_load(array('uid' => $account->uid));
      _profile_update_user_fields($fields, $account);
      $output .= theme('profile_listing', $account, $fields);
Dries's avatar
   
Dries committed
224
225
226
227
    }
    $output .= '</div>';
    $output .= theme('pager', NULL, 20);

228
    drupal_set_title(t('user list'));
Dries's avatar
   
Dries committed
229
    return $output;
Dries's avatar
   
Dries committed
230
  }
Dries's avatar
   
Dries committed
231
}
Dries's avatar
   
Dries committed
232

Dries's avatar
   
Dries committed
233
function profile_load_profile(&$user) {
Steven Wittens's avatar
Steven Wittens committed
234
  $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
235
236
  while ($field = db_fetch_object($result)) {
    if (empty($user->{$field->name})) {
Steven Wittens's avatar
Steven Wittens committed
237
      $user->{$field->name} = _profile_field_serialize($field->type) ? unserialize($field->value) : $field->value;
Dries's avatar
   
Dries committed
238
    }
Dries's avatar
   
Dries committed
239
  }
Dries's avatar
   
Dries committed
240
241
}

242
function profile_save_profile(&$edit, &$user, $category) {
243
  if ($_GET['q'] == 'user/register' || $_GET['q'] == 'admin/user/create') {
244
    $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
245
246
  }
  else {
247
    $result = db_query("SELECT fid, name, type FROM {profile_fields} WHERE LOWER(category) = LOWER('%s') AND visibility != %d", $category, PROFILE_HIDDEN);
248
    // We use LOWER('%s') instead of PHP's strtolower() to avoid UTF-8 conversion issues.
Dries's avatar
   
Dries committed
249
  }
Dries's avatar
   
Dries committed
250
  while ($field = db_fetch_object($result)) {
Steven Wittens's avatar
Steven Wittens committed
251
252
253
    if (_profile_field_serialize($field->type)) {
       $edit[$field->name] = serialize($edit[$field->name]);
    }
254
255
    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]);
256
    // Mark field as handled (prevents saving to user->data).
257
    $edit[$field->name] = NULL;
Dries's avatar
   
Dries committed
258
  }
Dries's avatar
   
Dries committed
259
260
}

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

Dries's avatar
Dries committed
265
266
267
  if ($field->fid == 18 || $field->fid == 19 || $field->fid == 20) {
    return;
  }
Dries's avatar
Dries committed
268

269
270
271
  if ($value = $user->{$field->name}) {
    switch ($field->type) {
      case 'textfield':
272
        return check_plain($value);
273
      case 'textarea':
274
        return check_markup($value);
275
      case 'selection':
276
        return $browse ? l($value, 'profile/'. drupal_urlencode($field->name) .'/'. drupal_urlencode($value)) : check_plain($value);
277
      case 'checkbox':
278
        return $browse ? l($field->title, 'profile/'. drupal_urlencode($field->name)) : check_plain($field->title);
279
      case 'url':
280
        return '<a href="'. check_url($value) .'">'. check_plain($value) .'</a>';
Steven Wittens's avatar
Steven Wittens committed
281
282
283
284
285
286
287
288
      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']),
289
                         'M' => map_month($value['month']),
Steven Wittens's avatar
Steven Wittens committed
290
291
                         'Y' => $value['year']);
        return strtr($format, $replace);
292
      case 'list':
Dries's avatar
   
Dries committed
293
        $values = split("[,\n\r]", $value);
Dries's avatar
Dries committed
294
295
        $fields = array();
        foreach ($values as $value) {
Steven Wittens's avatar
Steven Wittens committed
296
          if ($value = trim($value)) {
297
            $fields[] = $browse ? l($value, "profile/". drupal_urlencode($field->name) ."/". drupal_urlencode($value)) : check_plain($value);
Dries's avatar
Dries committed
298
299
300
          }
        }
        return implode(', ', $fields);
301
302
303
304
    }
  }
}

Dries's avatar
   
Dries committed
305
function profile_view_profile($user) {
Dries's avatar
   
Dries committed
306

307
  profile_load_profile($user);
Dries's avatar
   
Dries committed
308

Steven Wittens's avatar
Steven Wittens committed
309
310
  // Show private fields to administrators and people viewing their own account.
  if (user_access('administer users') || $GLOBALS['user']->uid == $user->uid) {
311
    $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d ORDER BY category, weight', PROFILE_HIDDEN);
Steven Wittens's avatar
Steven Wittens committed
312
313
  }
  else {
314
    $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
315
316
  }

Dries's avatar
   
Dries committed
317
  while ($field = db_fetch_object($result)) {
318
    if ($value = profile_view_field($user, $field)) {
Steven Wittens's avatar
Steven Wittens committed
319
      $description = ($field->visibility == PROFILE_PRIVATE) ? t('The content of this field is private and only visible to yourself.') : '';
320
      $title = ($field->type != 'checkbox') ? check_plain($field->title) : '';
321
322
      $form = array('#title' => $title, '#value' => $value, '#description' => $description);
      $fields[$field->category][$field->name] = theme('item', $form);
Dries's avatar
   
Dries committed
323
324
325
    }
  }

Dries's avatar
Dries committed
326
  return $fields;
Dries's avatar
   
Dries committed
327
}
Dries's avatar
   
Dries committed
328

329
330
function _profile_form_explanation($field) {
  $output = $field->explanation;
Dries's avatar
   
Dries committed
331

332
  if ($field->type == 'list') {
Dries's avatar
   
Dries committed
333
    $output .= ' '. t('Put each item on a separate line or separate them by commas.  No HTML allowed.');
334
335
336
337
338
339
340
341
342
343
344
  }

  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) {

345
  if ($_GET['q'] == 'user/register' || $_GET['q'] == 'admin/user/create') {
Dries's avatar
   
Dries committed
346
347
348
    $result = db_query('SELECT * FROM {profile_fields} WHERE register = 1 ORDER BY category, weight');
  }
  else {
349
350
    $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
351
  }
352

Dries's avatar
   
Dries committed
353
  while ($field = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
354
    $category = $field->category;
355
356
357
    if (!isset($fields[$category])) {
      $fields[$category] = array('#type' => 'fieldset', '#title' => $category, '#weight' => $w++);
    }
Dries's avatar
   
Dries committed
358
359
    switch ($field->type) {
      case 'textfield':
360
      case 'url':
361
        $fields[$category][$field->name] = array('#type' => 'textfield', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#maxlength' => 255, '#description' => _profile_form_explanation($field), '#required' => $field->required);
Dries's avatar
   
Dries committed
362
        break;
363
      case 'textarea':
364
        $fields[$category][$field->name] = array('#type' => 'textarea', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#description' => _profile_form_explanation($field), '#required' => $field->required);
Dries's avatar
Dries committed
365
366
        break;
      case 'list':
367
        $fields[$category][$field->name] = array('#type' => 'textarea', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#description' => _profile_form_explanation($field), '#required' => $field->required);
Dries's avatar
   
Dries committed
368
        break;
369
      case 'checkbox':
370
        $fields[$category][$field->name] = array('#type' => 'checkbox', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#description' => _profile_form_explanation($field), '#required' => $field->required);
Dries's avatar
   
Dries committed
371
372
373
        break;
      case 'selection':
        $options = array('--');
Dries's avatar
   
Dries committed
374
        $lines = split("[,\n\r]", $field->options);
Dries's avatar
   
Dries committed
375
376
377
378
379
        foreach ($lines as $line) {
          if ($line = trim($line)) {
            $options[$line] = $line;
          }
        }
380
        $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
381
        break;
Steven Wittens's avatar
Steven Wittens committed
382
      case 'date':
383
        $fields[$category][$field->name] = array('#type' => 'date', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#description' => _profile_form_explanation($field), '#required' => $field->required);
Steven Wittens's avatar
Steven Wittens committed
384
        break;
Dries's avatar
   
Dries committed
385
386
    }
  }
387
  return $fields;
Dries's avatar
   
Dries committed
388
389
}

390
391
392
393
394
395
396
397
398
399
400
/**
 * 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;
    }
  }
}

401
function profile_validate_profile($edit, $category) {
Dries's avatar
   
Dries committed
402

403
  if ($_GET['q'] == 'user/register' || $_GET['q'] == 'admin/user/create') {
Dries's avatar
   
Dries committed
404
405
406
    $result = db_query('SELECT * FROM {profile_fields} WHERE register = 1 ORDER BY category, weight');
  }
  else {
407
408
    $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
409
  }
410
411

  while ($field = db_fetch_object($result)) {
412
    if ($edit[$field->name]) {
Dries's avatar
   
Dries committed
413
414
      if ($field->type == 'url') {
        if (!valid_url($edit[$field->name], true)) {
415
416
          form_set_error($field->name, t('The value provided for %field is not a valid URL.', array('%field' => theme('placeholder', $field->title))));
        }
417
418
      }
    }
419
    else if ($field->required && !user_access('administer users')) {
420
      form_set_error($field->name, t('The field %field is required.', array('%field' => theme('placeholder', $field->title))));
Dries's avatar
   
Dries committed
421
    }
422
423
424
425
426
  }

  return $edit;
}

427
428
429
function profile_categories() {
  $result = db_query("SELECT DISTINCT(category) FROM {profile_fields}");
  while ($category = db_fetch_object($result)) {
430
    $data[] = array('name' => check_plain($category->category), 'title' => $category->category, 'weight' => 3);
431
432
433
434
  }
  return $data;
}

Dries's avatar
   
Dries committed
435
436
437
/**
 * Implementation of hook_user().
 */
438
function profile_user($type, &$edit, &$user, $category = NULL) {
Dries's avatar
   
Dries committed
439
440
441
  switch ($type) {
    case 'load':
      return profile_load_profile($user);
Dries's avatar
   
Dries committed
442
443
    case 'register':
      return profile_form_profile($edit, $user, $category);
Dries's avatar
   
Dries committed
444
    case 'update':
445
    case 'insert':
446
      return profile_save_profile($edit, $user, $category);
Dries's avatar
   
Dries committed
447
448
    case 'view':
      return profile_view_profile($user);
449
    case 'form':
450
      return profile_form_profile($edit, $user, $category);
Dries's avatar
   
Dries committed
451
    case 'validate':
452
453
454
      return profile_validate_profile($edit, $category);
    case 'categories':
      return profile_categories();
Dries's avatar
   
Dries committed
455
456
  }
}
Dries's avatar
   
Dries committed
457

Dries's avatar
   
Dries committed
458
function profile_validate_form($edit) {
Dries's avatar
   
Dries committed
459

Dries's avatar
   
Dries committed
460
  // Validate the title:
Dries's avatar
   
Dries committed
461

Dries's avatar
   
Dries committed
462
  if (!$edit['title']) {
Dries's avatar
   
Dries committed
463
    form_set_error('title', t('You must enter a title.'));
Dries's avatar
   
Dries committed
464
465
  }

Dries's avatar
   
Dries committed
466
  // Validate the 'form name':
Dries's avatar
   
Dries committed
467

Dries's avatar
   
Dries committed
468
  if (eregi('[^a-z0-9_-]', $edit['name'])) {
Dries's avatar
   
Dries committed
469
    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
470
471
  }

Dries's avatar
   
Dries committed
472
  if (in_array($edit['name'], user_fields())) {
Dries's avatar
   
Dries committed
473
    form_set_error('name', t('The specified form name is reserved for use by Drupal.'));
Dries's avatar
   
Dries committed
474
475
  }

Dries's avatar
   
Dries committed
476
477
  // Validate the category:
  if (!$edit['category']) {
Dries's avatar
   
Dries committed
478
    form_set_error('category', t('You must enter a category.'));
Dries's avatar
   
Dries committed
479
  }
Dries's avatar
   
Dries committed
480
481
}

Dries's avatar
   
Dries committed
482
483
484
/**
 * Menu callback; adds a new field to all user profiles.
 */
Dries's avatar
   
Dries committed
485
486
487
function profile_admin_add($type) {
  if ($_POST['op']) {
    $data = $_POST['edit'];
Dries's avatar
   
Dries committed
488

Dries's avatar
   
Dries committed
489
490
491
    // Validate the form:
    profile_validate_form($data);

492
    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
493
      form_set_error('title', t('The specified title is already in use.'));
494
    }
Dries's avatar
   
Dries committed
495
496

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

Dries's avatar
   
Dries committed
500
    if (!form_get_errors()) {
Dries's avatar
   
Dries committed
501
      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
502

Dries's avatar
   
Dries committed
503
504
      cache_clear_all();

Steven Wittens's avatar
Steven Wittens committed
505
      drupal_set_message(t('The field has been created.'));
506
      drupal_goto('admin/settings/profile');
Dries's avatar
   
Dries committed
507
    }
Dries's avatar
   
Dries committed
508
  }
Dries's avatar
   
Dries committed
509
510
511
512
  else {
    $data = array('name' => 'profile_');
  }

513
  drupal_set_title(t('Add new %type', array('%type' => _profile_field_types($type))));
Dries's avatar
   
Dries committed
514
  return _profile_field_form($type, $data);
Dries's avatar
   
Dries committed
515
516
}

Dries's avatar
   
Dries committed
517
518
519
/**
 * Menu callback; displays the profile field editing form.
 */
Dries's avatar
   
Dries committed
520
function profile_admin_edit($fid) {
Dries's avatar
   
Dries committed
521

Dries's avatar
   
Dries committed
522
523
  if ($_POST['op']) {
    $data = $_POST['edit'];
Dries's avatar
   
Dries committed
524

Dries's avatar
   
Dries committed
525
526
    // Validate form:
    profile_validate_form($data);
Dries's avatar
   
Dries committed
527

Dries's avatar
   
Dries committed
528
    if (!form_get_errors()) {
Dries's avatar
   
Dries committed
529
      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
530

Dries's avatar
   
Dries committed
531
532
      cache_clear_all();

Steven Wittens's avatar
Steven Wittens committed
533
      drupal_set_message(t('The field has been updated.'));
534
      drupal_goto('admin/settings/profile');
Dries's avatar
   
Dries committed
535
    }
Dries's avatar
   
Dries committed
536
537
  }
  else {
Dries's avatar
   
Dries committed
538
    $data = db_fetch_array(db_query('SELECT * FROM {profile_fields} WHERE fid = %d', $fid));
Dries's avatar
   
Dries committed
539
540
  }

541
  drupal_set_title(t('Edit %type', array('%type' => $data['type'])));
Dries's avatar
   
Dries committed
542
  return _profile_field_form($data['type'], $data);
Dries's avatar
   
Dries committed
543
544
}

Dries's avatar
   
Dries committed
545
546
547
/**
 * Menu callback; deletes a field from all user profiles.
 */
Dries's avatar
   
Dries committed
548
function profile_admin_delete($fid) {
549
550
551
552
553
554
555
556
  $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 {
557
    return confirm_form('profile_confirm_delete', $form, t('Do you want to remove the field %field?',
558
                    array('%field' => $field->title)),
559
                  'admin/settings/profile', '', t('Delete'), t('Cancel'));
560
  }
Dries's avatar
   
Dries committed
561
562
}

Dries's avatar
   
Dries committed
563
function _profile_field_form($type, $edit = array()) {
564

565
  $form['fields'] = array('#type' => 'fieldset', '#title' => t('Field settings'));
566
567
568
  $form['fields']['category'] = array('#type' => 'textfield', '#title' => t('Category'), '#default_value' => $edit['category'], '#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'], '#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'], '#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
569
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".'));
570
  $form['fields']['explanation'] = array('#type' => 'textarea', '#title' => t('Explanation'), '#default_value' => $edit['explanation'], '#description' => t('An optional explanation to go with the new field.  The explanation will be shown to the user.'));
Dries's avatar
   
Dries committed
571
  if ($type == 'selection') {
572
    $form['fields']['options'] = array('#type' => 'textarea', '#title' => t('Selection options'), '#default_value' => $edit['options'], '#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
573
  }
574
575
  $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
576
  if ($type == 'selection' || $type == 'list') {
577
    $form['fields']['page'] = array('#type' => 'textfield', '#title' => t('Page title'), '#default_value' => $edit['page'], '#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
578
579
  }
  else {
580
    $form['fields']['page'] = array('#type' => 'textfield', '#title' => t('Page title'), '#default_value' => $edit['page'], '#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
581
  }
582
583
  $form['fields']['required'] = array('#type' => 'checkbox', '#title' => t('The user must enter a value.'), '#default_value' => $edit['required']);
  $form['fields']['register'] = array('#type' => 'checkbox', '#title' => t('Visible in user registration form.'), '#default_value' => $edit['register']);
584
  $form['submit'] = array('#type' => 'submit', '#value' => t('Save field'));
Dries's avatar
   
Dries committed
585

586
  return drupal_get_form('_profile_field_form', $form);
Dries's avatar
   
Dries committed
587
588
}

Dries's avatar
   
Dries committed
589
590
591
/**
 * Menu callback; display a listing of all editable profile fields.
 */
Dries's avatar
   
Dries committed
592
593
594
function profile_admin_overview() {

  $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight');
Steven Wittens's avatar
Steven Wittens committed
595
  $rows = array();
Dries's avatar
   
Dries committed
596
  while ($field = db_fetch_object($result)) {
597
    $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
598
599
600
  }
  if (count($rows) == 0) {
    $rows[] = array(array('data' => t('No fields defined.'), 'colspan' => '6'));
Dries's avatar
   
Dries committed
601
  }
Dries's avatar
   
Dries committed
602

Dries's avatar
   
Dries committed
603
  $header = array(t('Title'), t('Name'), t('Type'), t('Category'), array('data' => t('Operations'), 'colspan' => '2'));
Dries's avatar
   
Dries committed
604
605

  $output  = theme('table', $header, $rows);
Steven Wittens's avatar
Steven Wittens committed
606
  $output .= '<h2>'. t('Add new field') .'</h2>';
Dries's avatar
   
Dries committed
607
608
  $output .= '<ul>';
  foreach (_profile_field_types() as $key => $value) {
609
    $output .= '<li>'. l($value, "admin/settings/profile/add/$key") .'</li>';
Dries's avatar
   
Dries committed
610
  }
Dries's avatar
   
Dries committed
611
612
  $output .= '</ul>';

Dries's avatar
   
Dries committed
613
  return $output;
Dries's avatar
   
Dries committed
614
615
}

616
function theme_profile_block($account, $fields = array()) {
617

618
  $output .= theme('user_picture', $account);
619
620

  foreach ($fields as $field) {
621
    if ($field->value) {
622
      $output .= "<p><strong>$field->title:</strong><br />$field->value</p>\n";
623
624
625
626
627
628
    }
  }

  return $output;
}

629
function theme_profile_listing($account, $fields = array()) {
Dries's avatar
   
Dries committed
630
631

  $output  = "<div class=\"profile\">\n";
632
633
  $output .= theme('user_picture', $account);
  $output .= ' <div class="name">'. theme('username', $account) ."</div>\n";
Dries's avatar
   
Dries committed
634
635

  foreach ($fields as $field) {
636
    if ($field->value) {
637
      $output .= " <div class=\"field\">$value</div>\n";
Dries's avatar
   
Dries committed
638
639
    }
  }
Dries's avatar
   
Dries committed
640
641
642
643
644
645
646

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

  return $output;
}

function _profile_field_types($type = NULL) {
Steven Wittens's avatar
Steven Wittens committed
647
648
649
650
651
652
653
  $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
654
  return isset($type) ? $types[$type] : $types;
Dries's avatar
   
Dries committed
655
656
}

Steven Wittens's avatar
Steven Wittens committed
657
658
659
660
function _profile_field_serialize($type = NULL) {
  return $type == 'date';
}

661