#10012 - Profile.module:

- Restoring broken update path.
- Adding birthday/date function back, with update path.
- Show private fields when viewing your own profile, or for admins.
- Do not allow browsing of private fields for non admins (403)
- Throw a 404 for browsing unbrowsable fields, rather than an SQL error
- Fixing input processing: nothing is filtered twice anymore, and I replaced several strip_tags with specialchars (more flexible).
- Minor admin UI tweaks + added friendly field type names.
4.5.x
Steven Wittens 2004-08-14 11:54:31 +00:00
parent a3bd80b267
commit 6a380525d4
3 changed files with 337 additions and 199 deletions

View File

@ -789,17 +789,42 @@ function update_79() {
function update_80() {
// Add a 'created' field to the users table:
$ret[] = update_sql('ALTER TABLE {users} ADD created INT(11) NOT NULL');
$ret[] = update_sql('ALTER TABLE {users} CHANGE timestamp changed INT(11) NOT NULL');
// Add some indices to speed up the update process:
$ret[] = update_sql('ALTER TABLE {comments} ADD index (timestamp)');
$ret[] = update_sql('ALTER TABLE {node} ADD index (created)');
if ($GLOBALS["db_type"] == "mysql") {
// Add a 'created' field to the users table:
$ret[] = update_sql('ALTER TABLE {users} ADD created INT(11) NOT NULL');
$ret[] = update_sql('ALTER TABLE {users} CHANGE timestamp changed INT(11) NOT NULL');
// Assign everyone a created timestamp to begin with:
$ret[] = update_sql("UPDATE {users} SET created = changed WHERE created = ''");
// Print a status message:"
print '<p>Note: this might take a while ...</p>';
// Try updating the user records using the comment table:
$result = db_query('SELECT DISTINCT(u.uid) FROM {comments} c LEFT JOIN {users} u ON c.uid = u.uid WHERE c.timestamp < u.created');
while ($account = db_fetch_object($result)) {
// Retrieve the proper timestamp:
$timestamp = db_result(db_query('SELECT MIN(timestamp) FROM {comments} WHERE uid = %d', $account->uid));
// Add profile module related tables:
$ret[] = update_sql("CREATE TABLE {profile_fields} (
// Update this user record as well as older records with an older timestamp:
db_query('UPDATE {users} SET created = %d WHERE created > %d AND uid <= %d', $timestamp, $timestamp, $account->uid);
}
// Try updating the user records using the node table:
$result = db_query('SELECT DISTINCT(u.uid) FROM {node} n LEFT JOIN {users} u ON n.uid = u.uid WHERE n.created < u.created');
while ($account = db_fetch_object($result)) {
// Retrieve the proper timestamp:
$timestamp = db_result(db_query('SELECT MIN(created) FROM {node} WHERE uid = %d', $account->uid));
// Update this user record as well as older records with an older timestamp:
db_query('UPDATE {users} SET created = %d WHERE created > %d AND uid <= %d', $timestamp, $timestamp, $account->uid);
}
// Add profile module related tables:
$ret[] = update_sql("CREATE TABLE {profile_fields} (
fid int(10) NOT NULL auto_increment,
title varchar(255) default NULL,
name varchar(128) default NULL,
@ -814,150 +839,81 @@ function update_80() {
PRIMARY KEY (fid)
);");
$ret[] = update_sql("CREATE TABLE {profile_values} (
$ret[] = update_sql("CREATE TABLE {profile_values} (
fid int(11) unsigned default '0',
uid int(11) unsigned default '0',
value text,
KEY uid (uid),
KEY fid (fid)
);");
// Add some indices to speed up the update process:
$ret[] = update_sql('ALTER TABLE {comments} ADD index (timestamp)');
$ret[] = update_sql('ALTER TABLE {node} ADD index (created)');
// Assign everyone a created timestamp to begin with:
$ret[] = update_sql("UPDATE {users} SET created = changed WHERE created = ''");
}
else {// pgsql
// Migrate the old profile data to the new scheme:
$fields = array(
array("Name", "realname", "textfield", NULL, 0),
array("Address", "address", "textfield", NULL, 0),
array("City", "city", "textfield", NULL, 0),
array("State, province or region", "state", "textfield", NULL, 0),
array("Zip or postal code", "zip", "textfield", NULL, 0),
array("Country", "country", "textfield", NULL, 1),
array("Gender", "gender", "selection", "male\nfemale", 1),
array("Job title", "job", "textfield", NULL, 0),
array("ICQ messenger ID", "icq", "textfield", NULL, 0),
array("MSN messenger ID", "msn", "textfield", NULL, 0),
array("Yahoo messenger ID", "yahoo", "textfield", NULL, 0),
array("AIM messenger ID", "aim", "textfield", NULL, 0),
array("URL of homepage", "homepage", "url", NULL, 1),
array("Biography", "biography", "textarea", NULL, 0),
array("Interests", "interests", "textarea", NULL, 0),
array("Public key", "publickey", "textarea", NULL, 0),
array("Birthday", "birthday", "date", NULL, 0)
);
$ret[] = update_sql('ALTER TABLE {users} RENAME timestamp TO changed');
$ret[] = update_sql('ALTER TABLE {users} ADD created integer');
$ret[] = update_sql('UPDATE {users} SET created = 0');
$ret[] = update_sql('ALTER TABLE {users} ALTER COLUMN created SET NOT NULL');
$ret[] = update_sql('ALTER TABLE {users} ALTER COLUMN created SET DEFAULT 0');
// Add profile module related tables:
$ret[] = update_sql("CREATE TABLE {profile_fields} (
fid serial,
title varchar(255) default NULL,
name varchar(128) default NULL,
explanation TEXT default NULL,
category varchar(255) default NULL,
type varchar(128) default NULL,
weight smallint DEFAULT '0' NOT NULL,
overview smallint DEFAULT '0' NOT NULL,
options text,
UNIQUE (name),
PRIMARY KEY (fid)
);");
$ret[] = update_sql("CREATE INDEX profile_fields_category ON {profile_fields} (category);");
$ret[] = update_sql("CREATE TABLE {profile_values} (
fid integer default '0',
uid integer default '0',
value text
);");
$ret[] = update_sql("CREATE INDEX profile_values_uid ON profile_values (uid);");
$ret[] = update_sql("CREATE INDEX profile_values_fid ON profile_values (fid);");
// Add some indices to speed up the update process:
$ret[] = update_sql('CREATE INDEX comments_timestamp ON {comments} (timestamp)');
$ret[] = update_sql('CREATE INDEX node_created on {node} (created)');
// Assign everyone a created timestamp to begin with:
$ret[] = update_sql("UPDATE {users} SET created = changed WHERE created = 0");
// Remove existing data (debug mode):
db_query('DELETE FROM {profile_fields}');
db_query('DELETE FROM {profile_values}');
foreach ($fields as $field) {
db_query("INSERT INTO {profile_fields} (title, name, type, category, options, overview) VALUES ('%s', '%s', '%s', 'Personal information', '%s', %d)", $field[0], $field[1], $field[2], $field[3], $field[4]);
}
db_query("ALTER TABLE {users} ADD picture varchar(255) NOT NULL DEFAULT ''");
// Print a status message:"
print '<p>Note: this might take a while ...</p>';
// Try updating the user records using the comment table:
$result = db_query('SELECT DISTINCT(u.uid) FROM {comments} c LEFT JOIN {users} u ON c.uid = u.uid WHERE c.timestamp < u.created');
$result = db_query("SELECT uid FROM {users}");
while ($account = db_fetch_object($result)) {
// Retrieve the proper timestamp:
$timestamp = db_result(db_query('SELECT MIN(timestamp) FROM {comments} WHERE uid = %d', $account->uid));
// Try updating the user records using the node table:
$result = db_query('SELECT DISTINCT(u.uid) FROM {node} n LEFT JOIN {users} u ON n.uid = u.uid WHERE n.created < u.created');
while ($account = db_fetch_object($result)) {
// Retrieve the proper timestamp:
$timestamp = db_result(db_query('SELECT MIN(created) FROM {node} WHERE uid = %d', $account->uid));
// Update this user record as well as older records with an older timestamp:
db_query('UPDATE {users} SET created = %d WHERE created > %d AND uid <= %d', $timestamp, $timestamp, $account->uid);
}
// Update this user record as well as older records with an older timestamp:
db_query('UPDATE {users} SET created = %d WHERE created > %d AND uid <= %d', $timestamp, $timestamp, $account->uid);
}
// Migrate the old profile data to the new scheme:
$fields = array(
array("Name", "realname", "textfield", NULL, 0),
array("Address", "address", "textfield", NULL, 0),
array("City", "city", "textfield", NULL, 0),
array("State, province or region", "state", "textfield", NULL, 0),
array("Zip or postal code", "zip", "textfield", NULL, 0),
array("Country", "country", "textfield", NULL, 1),
array("Gender", "gender", "selection", "male\nfemale", 1),
array("Job title", "job", "textfield", NULL, 0),
array("ICQ messenger ID", "icq", "textfield", NULL, 0),
array("MSN messenger ID", "msn", "textfield", NULL, 0),
array("Yahoo messenger ID", "yahoo", "textfield", NULL, 0),
array("AIM messenger ID", "aim", "textfield", NULL, 0),
array("URL of homepage", "homepage", "url", NULL, 1),
array("Biography", "biography", "textarea", NULL, 0),
array("Interests", "interests", "textarea", NULL, 0),
array("Public key", "publickey", "textarea", NULL, 0)
);
// Remove existing data (debug mode):
db_query('DELETE FROM {profile_fields}');
db_query('DELETE FROM {profile_values}');
// Load the user record:
$account = user_load(array('uid' => $account->uid));
$edit = array();
// Modify the user record:
foreach ($fields as $field) {
db_query("INSERT INTO {profile_fields} (title, name, type, category, options, overview) VALUES ('%s', '%s', '%s', 'Personal information', '%s', %d)", $field[0], $field[1], $field[2], $field[3], $field[4]);
}
if ($GLOBALS["db_type"] == "mysql") {
db_query("ALTER TABLE {users} ADD picture varchar(255) NOT NULL DEFAULT ''");
}
else {
db_query("ALTER TABLE {users} ADD picture varchar(255)");
db_query("UPDATE {users} SET picture = ''");
db_query("ALTER TABLE {users} ALTER COLUMN picture SET NOT NULL");
db_query("ALTER TABLE {users} ALTER COLUMN picture SET DEFAULT ''");
}
$result = db_query("SELECT uid FROM {users} WHERE uid > 0");
while ($account = db_fetch_object($result)) {
// Load the user record:
$account = user_load(array('uid' => $account->uid));
$edit = array();
// Modify the user record:
foreach ($fields as $field) {
$old = "profile_". $field[1];
$new = $field[1];
if ($account->$old) {
$edit[$new] = $account->$old;
}
unset($account->$old);
$old = "profile_". $field[1];
$new = $field[1];
if ($account->$old) {
$edit[$new] = $account->$old;
}
// Gender specific changes:
if ($edit['gender'] == 'f') $edit['gender'] = 'female';
if ($edit['gender'] == 'm') $edit['gender'] = 'male';
// Avatar specific changes:
if ($account->profile_avatar) {
$edit['picture'] = $account->profile_avatar;
}
unset($account->profile_avatar);
// Save the update record:
user_save($account, $edit, 'Personal information');
unset($account->$old);
}
// Birthday format change:
if ($edit['birthday']) {
$edit['birthday'] = array('day' => $edit['birthday'], 'month' => $account->profile_birthmonth, 'year' => $account->profile_birthyear);
unset($account->profile_birthmonth);
unset($account->profile_birthyear);
}
// Gender specific changes:
if ($edit['gender'] == 'f') $edit['gender'] = 'female';
if ($edit['gender'] == 'm') $edit['gender'] = 'male';
// Avatar specific changes:
if ($account->profile_avatar) {
$edit['picture'] = $account->profile_avatar;
}
unset($account->profile_avatar);
// Save the update record:
user_save($account, $edit);
}
return $ret;
}

View File

@ -1,8 +1,6 @@
<?php
// $Id$
// TODO: add a 'date' field so we can migrate the birthday information.
/**
* Flags to define the visibility of a profile field.
*/
@ -56,12 +54,18 @@ function profile_menu() {
*/
function profile_browse() {
$name = strip_tags(arg(1));
$value = strip_tags(arg(2));
$name = arg(1);
$value = arg(2);
$field = db_fetch_object(db_query("SELECT DISTINCT(fid), type, title, page FROM {profile_fields} WHERE name = '%s'", $name));
$field = db_fetch_object(db_query("SELECT DISTINCT(fid), type, title, page, visibility FROM {profile_fields} WHERE name = '%s'", $name));
if ($name && $field->fid) {
// Do not allow browsing of private fields by non-admins
if (!user_access('administer users') && $field->visibility == PROFILE_PRIVATE) {
drupal_access_denied();
return;
}
// Compile a list of fields to show
$fields = array();
$result = db_query('SELECT name, title, type FROM {profile_fields} WHERE fid != %d AND visibility = %d', $field->fid, PROFILE_PUBLIC_LISTINGS);
@ -80,6 +84,9 @@ function profile_browse() {
case 'list':
$query = "v.value LIKE '%%". check_query($value) ."%%'";
break;
default:
drupal_not_found();
return;
}
// Extract the affected users:
@ -127,17 +134,20 @@ function profile_browse() {
}
function profile_load_profile(&$user) {
$result = db_query('SELECT f.name, v.value FROM {profile_fields} f INNER JOIN {profile_values} v ON f.fid = v.fid WHERE uid = %d', $user->uid);
$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);
while ($field = db_fetch_object($result)) {
if (empty($user->{$field->name})) {
$user->{$field->name} = $field->value;
$user->{$field->name} = _profile_field_serialize($field->type) ? unserialize($field->value) : $field->value;
}
}
}
function profile_save_profile(&$edit, &$user, $category) {
$result = db_query("SELECT fid, name FROM {profile_fields} WHERE LOWER(category) = '%s'", strtolower($category));
$result = db_query("SELECT fid, name, type FROM {profile_fields} WHERE LOWER(category) = '%s'", strtolower($category));
while ($field = db_fetch_object($result)) {
if (_profile_field_serialize($field->type)) {
$edit[$field->name] = serialize($edit[$field->name]);
}
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]);
unset($edit[$field->name], $user->{$field->name});
@ -145,23 +155,38 @@ function profile_save_profile(&$edit, &$user, $category) {
}
function profile_view_field($user, $field) {
// Only allow browsing of private fields for admins
$browse = user_access('administer users') || $field->visibility != PROFILE_PRIVATE;
if ($value = $user->{$field->name}) {
switch ($field->type) {
case 'textfield':
return drupal_specialchars($value);
case 'textarea':
return check_output($value);
case 'selection':
return l($value, "profile/$field->name/". drupal_specialchars($value, ENT_QUOTES));
return $browse ? l(drupal_specialchars($value), "profile/$field->name/". check_url($value)) : drupal_specialchars($value);
case 'checkbox':
return l($field->title, "profile/$field->name");
return $browse ? l(drupal_specialchars($field->title), "profile/$field->name") : drupal_specialchars($field->title);
case 'url':
return '<a href="'. check_url(strip_tags($value)) .'">'. strip_tags($value) .'</a>';
return '<a href="'. check_url($value) .'">'. drupal_specialchars($value) .'</a>';
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);
case 'list':
$values = split("[,\n\r]", $value);
$fields = array();
foreach ($values as $value) {
if ($value = trim(strip_tags($value))) {
$fields[] = l($value, "profile/$field->name/". drupal_specialchars($value, ENT_QUOTES));
if ($value = trim($value)) {
$fields[] = $browse ? l(drupal_specialchars($value), "profile/$field->name/". check_url($value)) : drupal_specialchars($value);
}
}
return implode(', ', $fields);
@ -173,15 +198,19 @@ function profile_view_profile($user) {
profile_load_profile($user);
$result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d ORDER BY category, weight', PROFILE_PRIVATE);
// Show private fields to administrators and people viewing their own account.
if (user_access('administer users') || $GLOBALS['user']->uid == $user->uid) {
$result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight');
}
else {
$result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d ORDER BY category, weight', PROFILE_PRIVATE);
}
while ($field = db_fetch_object($result)) {
if ($value = profile_view_field($user, $field)) {
if ($field->type == 'checkbox') {
$fields[$field->category] .= "<p>$value</p>";
}
else {
$fields[$field->category] .= form_item($field->title, check_output($value));
}
$description = ($field->visibility == PROFILE_PRIVATE) ? t('The content of this field is private and only visible to yourself.') : '';
$title = ($field->type != 'checkbox') ? $field->title : '';
$fields[$field->category] .= form_item($title, $value, $description);
}
}
@ -232,6 +261,9 @@ function profile_form_profile($edit, $user, $category) {
$output .= form_select($field->title, $field->name, $edit[$field->name], $options, _profile_form_explanation($field), 0, 0, $field->required);
break;
case 'date':
$output .= _profile_date_field($field, $edit);
break;
}
}
@ -240,6 +272,54 @@ function profile_form_profile($edit, $user, $category) {
}
}
/**
* Helper function: output a date selector
*/
function _profile_date_field($field, $edit) {
// Default to current date
if (!isset($edit[$field->name])) {
$edit[$field->name] = array('day' => format_date(time(), 'custom', 'j'),
'month' => format_date(time(), 'custom', 'n'),
'year' => format_date(time(), 'custom', 'Y'));
}
// Determine the order of day, month, year in the site's chosen date format.
$format = variable_get('date_format_short', 'm/d/Y');
$sort = array();
$sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
$sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
$sort['year'] = strpos($format, 'Y');
asort($sort);
$order = array_keys($sort);
// Output multi-selector for date
$output = '<div class="container-inline">';
foreach ($order as $type) {
switch ($type) {
case 'day':
$options = drupal_map_assoc(range(1, 31));
break;
case 'month':
$options = drupal_map_assoc(range(1, 12), '_profile_map_month');
break;
case 'year':
$options = drupal_map_assoc(range(1900, 2050));
break;
}
$output .= form_select('', $field->name .']['. $type, $edit[$field->name][$type], $options, '', 0, 0);
}
$output .= '</div>';
return form_item($field->title, $output, _profile_form_explanation($field), NULL, $field->required);
}
/**
* 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);
}
function profile_validate_profile($edit, $category) {
$result = db_query("SELECT * FROM {profile_fields} WHERE LOWER(category) = '%s' ORDER BY weight", strtolower($category));
@ -316,9 +396,6 @@ function profile_validate_form($edit) {
* Menu callback; adds a new field to all user profiles.
*/
function profile_admin_add($type) {
$type = _profile_field_types($type);
if ($_POST['op']) {
$data = $_POST['edit'];
@ -326,17 +403,17 @@ function profile_admin_add($type) {
profile_validate_form($data);
if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE title = '%s'", $data['title']))) {
form_set_error('title', t('the specified title is already in use.'));
form_set_error('title', t('The specified title is already in use.'));
}
if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE name = '%s'", $data['name']))) {
form_set_error('name', t('the specified name is already in use.'));
form_set_error('name', t('The specified name is already in use.'));
}
if (!form_get_errors()) {
db_query("INSERT INTO {profile_fields} (title, name, explanation, category, type, weight, required, visibility, options, page) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', '%s')", $data['title'], $data['name'], $data['explanation'], $data['category'], $type, $data['weight'], $data['required'], $data['visibility'], $data['options'], $data['page']);
drupal_set_message(t('the field has been created.'));
drupal_set_message(t('The field has been created.'));
drupal_goto('admin/user/configure/profile');
}
}
@ -344,7 +421,7 @@ function profile_admin_add($type) {
$data = array('name' => 'profile_');
}
print theme('page', _profile_field_form($type, $data), t('Add new %type', array('%type' => $type)));
print theme('page', _profile_field_form($type, $data), t('Add new %type', array('%type' => _profile_field_types($type))));
}
/**
@ -361,7 +438,7 @@ function profile_admin_edit($fid) {
if (!form_get_errors()) {
db_query("UPDATE {profile_fields} SET title = '%s', name = '%s', explanation = '%s', category = '%s', weight = %d, required = %d, visibility = %d, options = '%s', page = '%s' WHERE fid = %d", $data['title'], $data['name'], $data['explanation'], $data['category'], $data['weight'], $data['required'], $data['visibility'], $data['options'], $data['page'], $fid);
drupal_set_message(t('the field has been updated.'));
drupal_set_message(t('The field has been updated.'));
drupal_goto('admin/user/configure/profile');
}
}
@ -377,7 +454,7 @@ function profile_admin_edit($fid) {
*/
function profile_admin_delete($fid) {
db_query('DELETE FROM {profile_fields} WHERE fid = %d', $fid);
drupal_set_message(t('the field has been deleted.'));
drupal_set_message(t('The field has been deleted.'));
drupal_goto('admin/user/configure/profile');
}
@ -413,17 +490,21 @@ Unless you know what you are doing, it is highly recommended that you prefix the
function profile_admin_overview() {
$result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight');
$rows = array();
while ($field = db_fetch_object($result)) {
$rows[] = array($field->title, $field->name, $field->type, $field->category, l(t('edit'), "admin/user/configure/profile/edit/$field->fid"), l(t('delete'), "admin/user/configure/profile/delete/$field->fid"));
$rows[] = array($field->title, $field->name, _profile_field_types($field->type), $field->category, l(t('edit'), "admin/user/configure/profile/edit/$field->fid"), l(t('delete'), "admin/user/configure/profile/delete/$field->fid"));
}
if (count($rows) == 0) {
$rows[] = array(array('data' => t('No fields defined.'), 'colspan' => '6'));
}
$header = array(t('title'), t('name'), t('type'), t('category'), array('data' => t('operations'), 'colspan' => '2'));
$output = theme('table', $header, $rows);
$output .= '<h2>'. t('Create new field') .'</h2>';
$output .= '<h2>'. t('Add new field') .'</h2>';
$output .= '<ul>';
foreach (_profile_field_types() as $key => $value) {
$output .= '<li>'. l(t('Add new %type', array('%type' => $value)), "admin/user/configure/profile/add/$key") .'</li>';
$output .= '<li>'. l($value, "admin/user/configure/profile/add/$key") .'</li>';
}
$output .= '</ul>';
@ -448,8 +529,18 @@ function theme_profile_profile($user, $fields = array()) {
}
function _profile_field_types($type = NULL) {
$types = array('textfield', 'textarea', 'checkbox', 'selection', 'list', 'url');
$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'));
return isset($type) ? $types[$type] : $types;
}
function _profile_field_serialize($type = NULL) {
return $type == 'date';
}
?>

View File

@ -1,8 +1,6 @@
<?php
// $Id$
// TODO: add a 'date' field so we can migrate the birthday information.
/**
* Flags to define the visibility of a profile field.
*/
@ -56,12 +54,18 @@ function profile_menu() {
*/
function profile_browse() {
$name = strip_tags(arg(1));
$value = strip_tags(arg(2));
$name = arg(1);
$value = arg(2);
$field = db_fetch_object(db_query("SELECT DISTINCT(fid), type, title, page FROM {profile_fields} WHERE name = '%s'", $name));
$field = db_fetch_object(db_query("SELECT DISTINCT(fid), type, title, page, visibility FROM {profile_fields} WHERE name = '%s'", $name));
if ($name && $field->fid) {
// Do not allow browsing of private fields by non-admins
if (!user_access('administer users') && $field->visibility == PROFILE_PRIVATE) {
drupal_access_denied();
return;
}
// Compile a list of fields to show
$fields = array();
$result = db_query('SELECT name, title, type FROM {profile_fields} WHERE fid != %d AND visibility = %d', $field->fid, PROFILE_PUBLIC_LISTINGS);
@ -80,6 +84,9 @@ function profile_browse() {
case 'list':
$query = "v.value LIKE '%%". check_query($value) ."%%'";
break;
default:
drupal_not_found();
return;
}
// Extract the affected users:
@ -127,17 +134,20 @@ function profile_browse() {
}
function profile_load_profile(&$user) {
$result = db_query('SELECT f.name, v.value FROM {profile_fields} f INNER JOIN {profile_values} v ON f.fid = v.fid WHERE uid = %d', $user->uid);
$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);
while ($field = db_fetch_object($result)) {
if (empty($user->{$field->name})) {
$user->{$field->name} = $field->value;
$user->{$field->name} = _profile_field_serialize($field->type) ? unserialize($field->value) : $field->value;
}
}
}
function profile_save_profile(&$edit, &$user, $category) {
$result = db_query("SELECT fid, name FROM {profile_fields} WHERE LOWER(category) = '%s'", strtolower($category));
$result = db_query("SELECT fid, name, type FROM {profile_fields} WHERE LOWER(category) = '%s'", strtolower($category));
while ($field = db_fetch_object($result)) {
if (_profile_field_serialize($field->type)) {
$edit[$field->name] = serialize($edit[$field->name]);
}
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]);
unset($edit[$field->name], $user->{$field->name});
@ -145,23 +155,38 @@ function profile_save_profile(&$edit, &$user, $category) {
}
function profile_view_field($user, $field) {
// Only allow browsing of private fields for admins
$browse = user_access('administer users') || $field->visibility != PROFILE_PRIVATE;
if ($value = $user->{$field->name}) {
switch ($field->type) {
case 'textfield':
return drupal_specialchars($value);
case 'textarea':
return check_output($value);
case 'selection':
return l($value, "profile/$field->name/". drupal_specialchars($value, ENT_QUOTES));
return $browse ? l(drupal_specialchars($value), "profile/$field->name/". check_url($value)) : drupal_specialchars($value);
case 'checkbox':
return l($field->title, "profile/$field->name");
return $browse ? l(drupal_specialchars($field->title), "profile/$field->name") : drupal_specialchars($field->title);
case 'url':
return '<a href="'. check_url(strip_tags($value)) .'">'. strip_tags($value) .'</a>';
return '<a href="'. check_url($value) .'">'. drupal_specialchars($value) .'</a>';
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);
case 'list':
$values = split("[,\n\r]", $value);
$fields = array();
foreach ($values as $value) {
if ($value = trim(strip_tags($value))) {
$fields[] = l($value, "profile/$field->name/". drupal_specialchars($value, ENT_QUOTES));
if ($value = trim($value)) {
$fields[] = $browse ? l(drupal_specialchars($value), "profile/$field->name/". check_url($value)) : drupal_specialchars($value);
}
}
return implode(', ', $fields);
@ -173,15 +198,19 @@ function profile_view_profile($user) {
profile_load_profile($user);
$result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d ORDER BY category, weight', PROFILE_PRIVATE);
// Show private fields to administrators and people viewing their own account.
if (user_access('administer users') || $GLOBALS['user']->uid == $user->uid) {
$result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight');
}
else {
$result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d ORDER BY category, weight', PROFILE_PRIVATE);
}
while ($field = db_fetch_object($result)) {
if ($value = profile_view_field($user, $field)) {
if ($field->type == 'checkbox') {
$fields[$field->category] .= "<p>$value</p>";
}
else {
$fields[$field->category] .= form_item($field->title, check_output($value));
}
$description = ($field->visibility == PROFILE_PRIVATE) ? t('The content of this field is private and only visible to yourself.') : '';
$title = ($field->type != 'checkbox') ? $field->title : '';
$fields[$field->category] .= form_item($title, $value, $description);
}
}
@ -232,6 +261,9 @@ function profile_form_profile($edit, $user, $category) {
$output .= form_select($field->title, $field->name, $edit[$field->name], $options, _profile_form_explanation($field), 0, 0, $field->required);
break;
case 'date':
$output .= _profile_date_field($field, $edit);
break;
}
}
@ -240,6 +272,54 @@ function profile_form_profile($edit, $user, $category) {
}
}
/**
* Helper function: output a date selector
*/
function _profile_date_field($field, $edit) {
// Default to current date
if (!isset($edit[$field->name])) {
$edit[$field->name] = array('day' => format_date(time(), 'custom', 'j'),
'month' => format_date(time(), 'custom', 'n'),
'year' => format_date(time(), 'custom', 'Y'));
}
// Determine the order of day, month, year in the site's chosen date format.
$format = variable_get('date_format_short', 'm/d/Y');
$sort = array();
$sort['day'] = max(strpos($format, 'd'), strpos($format, 'j'));
$sort['month'] = max(strpos($format, 'm'), strpos($format, 'M'));
$sort['year'] = strpos($format, 'Y');
asort($sort);
$order = array_keys($sort);
// Output multi-selector for date
$output = '<div class="container-inline">';
foreach ($order as $type) {
switch ($type) {
case 'day':
$options = drupal_map_assoc(range(1, 31));
break;
case 'month':
$options = drupal_map_assoc(range(1, 12), '_profile_map_month');
break;
case 'year':
$options = drupal_map_assoc(range(1900, 2050));
break;
}
$output .= form_select('', $field->name .']['. $type, $edit[$field->name][$type], $options, '', 0, 0);
}
$output .= '</div>';
return form_item($field->title, $output, _profile_form_explanation($field), NULL, $field->required);
}
/**
* 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);
}
function profile_validate_profile($edit, $category) {
$result = db_query("SELECT * FROM {profile_fields} WHERE LOWER(category) = '%s' ORDER BY weight", strtolower($category));
@ -316,9 +396,6 @@ function profile_validate_form($edit) {
* Menu callback; adds a new field to all user profiles.
*/
function profile_admin_add($type) {
$type = _profile_field_types($type);
if ($_POST['op']) {
$data = $_POST['edit'];
@ -326,17 +403,17 @@ function profile_admin_add($type) {
profile_validate_form($data);
if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE title = '%s'", $data['title']))) {
form_set_error('title', t('the specified title is already in use.'));
form_set_error('title', t('The specified title is already in use.'));
}
if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE name = '%s'", $data['name']))) {
form_set_error('name', t('the specified name is already in use.'));
form_set_error('name', t('The specified name is already in use.'));
}
if (!form_get_errors()) {
db_query("INSERT INTO {profile_fields} (title, name, explanation, category, type, weight, required, visibility, options, page) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', '%s')", $data['title'], $data['name'], $data['explanation'], $data['category'], $type, $data['weight'], $data['required'], $data['visibility'], $data['options'], $data['page']);
drupal_set_message(t('the field has been created.'));
drupal_set_message(t('The field has been created.'));
drupal_goto('admin/user/configure/profile');
}
}
@ -344,7 +421,7 @@ function profile_admin_add($type) {
$data = array('name' => 'profile_');
}
print theme('page', _profile_field_form($type, $data), t('Add new %type', array('%type' => $type)));
print theme('page', _profile_field_form($type, $data), t('Add new %type', array('%type' => _profile_field_types($type))));
}
/**
@ -361,7 +438,7 @@ function profile_admin_edit($fid) {
if (!form_get_errors()) {
db_query("UPDATE {profile_fields} SET title = '%s', name = '%s', explanation = '%s', category = '%s', weight = %d, required = %d, visibility = %d, options = '%s', page = '%s' WHERE fid = %d", $data['title'], $data['name'], $data['explanation'], $data['category'], $data['weight'], $data['required'], $data['visibility'], $data['options'], $data['page'], $fid);
drupal_set_message(t('the field has been updated.'));
drupal_set_message(t('The field has been updated.'));
drupal_goto('admin/user/configure/profile');
}
}
@ -377,7 +454,7 @@ function profile_admin_edit($fid) {
*/
function profile_admin_delete($fid) {
db_query('DELETE FROM {profile_fields} WHERE fid = %d', $fid);
drupal_set_message(t('the field has been deleted.'));
drupal_set_message(t('The field has been deleted.'));
drupal_goto('admin/user/configure/profile');
}
@ -413,17 +490,21 @@ Unless you know what you are doing, it is highly recommended that you prefix the
function profile_admin_overview() {
$result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight');
$rows = array();
while ($field = db_fetch_object($result)) {
$rows[] = array($field->title, $field->name, $field->type, $field->category, l(t('edit'), "admin/user/configure/profile/edit/$field->fid"), l(t('delete'), "admin/user/configure/profile/delete/$field->fid"));
$rows[] = array($field->title, $field->name, _profile_field_types($field->type), $field->category, l(t('edit'), "admin/user/configure/profile/edit/$field->fid"), l(t('delete'), "admin/user/configure/profile/delete/$field->fid"));
}
if (count($rows) == 0) {
$rows[] = array(array('data' => t('No fields defined.'), 'colspan' => '6'));
}
$header = array(t('title'), t('name'), t('type'), t('category'), array('data' => t('operations'), 'colspan' => '2'));
$output = theme('table', $header, $rows);
$output .= '<h2>'. t('Create new field') .'</h2>';
$output .= '<h2>'. t('Add new field') .'</h2>';
$output .= '<ul>';
foreach (_profile_field_types() as $key => $value) {
$output .= '<li>'. l(t('Add new %type', array('%type' => $value)), "admin/user/configure/profile/add/$key") .'</li>';
$output .= '<li>'. l($value, "admin/user/configure/profile/add/$key") .'</li>';
}
$output .= '</ul>';
@ -448,8 +529,18 @@ function theme_profile_profile($user, $fields = array()) {
}
function _profile_field_types($type = NULL) {
$types = array('textfield', 'textarea', 'checkbox', 'selection', 'list', 'url');
$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'));
return isset($type) ? $types[$type] : $types;
}
function _profile_field_serialize($type = NULL) {
return $type == 'date';
}
?>