diff --git a/database/database.mysql b/database/database.mysql index 7496db8f215..88126a0fb24 100644 --- a/database/database.mysql +++ b/database/database.mysql @@ -151,7 +151,7 @@ CREATE TABLE boxes ( info varchar(128) NOT NULL default '', type tinyint(2) NOT NULL default '0', PRIMARY KEY (bid), - UNIQUE KEY subject (title), + UNIQUE KEY title (title), UNIQUE KEY info (info) ) TYPE=MyISAM; @@ -363,7 +363,7 @@ CREATE TABLE profile_fields ( type varchar(128) default NULL, weight tinyint(1) DEFAULT '0' NOT NULL, required tinyint(1) DEFAULT '0' NOT NULL, - overview tinyint(1) DEFAULT '0' NOT NULL, + visibility tinyint(1) DEFAULT '0' NOT NULL, options text, KEY category (category), UNIQUE KEY name (name), diff --git a/database/database.pgsql b/database/database.pgsql index bb4ae1e3df3..73c737a33e8 100644 --- a/database/database.pgsql +++ b/database/database.pgsql @@ -379,7 +379,7 @@ CREATE TABLE profile_fields ( type varchar(128) default NULL, weight smallint DEFAULT '0' NOT NULL, required smallint DEFAULT '0' NOT NULL, - overview smallint DEFAULT '0' NOT NULL, + visibility smallint DEFAULT '0' NOT NULL, options text, UNIQUE (name), PRIMARY KEY (fid) diff --git a/database/updates.inc b/database/updates.inc index ba66ff511ba..ef53af32612 100644 --- a/database/updates.inc +++ b/database/updates.inc @@ -60,7 +60,8 @@ $sql_updates = array( "2004-05-10" => "update_86", "2004-05-18" => "update_87", "2004-06-11" => "update_88", - "2004-06-18" => "update_89" + "2004-06-18" => "update_89", + "2004-06-27" => "update_90" ); function update_32() { @@ -1120,6 +1121,13 @@ function update_89() { return $ret; } +function update_90() { + $ret[] = update_sql("ALTER TABLE {profile_fields} CHANGE overview visibility INT(1) UNSIGNED DEFAULT '0' NOT NULL"); + $ret[] = update_sql("UPDATE {profile_fields} SET visibility = 2 WHERE visibility = 1"); + $ret[] = update_sql("UPDATE {profile_fields} SET visibility = 1 WHERE visibility = 0"); + return $ret; +} + function update_sql($sql) { $edit = $_POST["edit"]; $result = db_query($sql); diff --git a/modules/block.module b/modules/block.module index 455380f1270..358654a0101 100644 --- a/modules/block.module +++ b/modules/block.module @@ -357,20 +357,22 @@ function block_admin() { * Allow users to decide which custom blocks to display when they visit * the site. */ -function block_user($type, $edit, &$user) { +function block_user($type, $edit, &$user, $category = NULL) { switch ($type) { case 'form': - $result = db_query('SELECT * FROM {blocks} WHERE custom = %d ORDER BY module, delta', 1); + if ($category == 'account') { + $result = db_query('SELECT * FROM {blocks} WHERE custom = %d ORDER BY module, delta', 1); - while ($block = db_fetch_object($result)) { - $data = module_invoke($block->module, 'block', 'list'); - if ($data[$block->delta]['info']) { - $form .= form_checkbox($data[$block->delta]['info'], "block][$block->module][$block->delta", 1, isset($user->block[$block->module][$block->delta]) ? $user->block[$block->module][$block->delta] : $block->status); + while ($block = db_fetch_object($result)) { + $data = module_invoke($block->module, 'block', 'list'); + if ($data[$block->delta]['info']) { + $form .= form_checkbox($data[$block->delta]['info'], "block][$block->module][$block->delta", 1, isset($user->block[$block->module][$block->delta]) ? $user->block[$block->module][$block->delta] : $block->status); + } } - } - if (isset($form)) { - return array(t('Block configuration') => $form); + if (isset($form)) { + return array(array('title' => t('Block configuration'), 'data' => $form, 'weight' => 2)); + } } break; diff --git a/modules/block/block.module b/modules/block/block.module index 455380f1270..358654a0101 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -357,20 +357,22 @@ function block_admin() { * Allow users to decide which custom blocks to display when they visit * the site. */ -function block_user($type, $edit, &$user) { +function block_user($type, $edit, &$user, $category = NULL) { switch ($type) { case 'form': - $result = db_query('SELECT * FROM {blocks} WHERE custom = %d ORDER BY module, delta', 1); + if ($category == 'account') { + $result = db_query('SELECT * FROM {blocks} WHERE custom = %d ORDER BY module, delta', 1); - while ($block = db_fetch_object($result)) { - $data = module_invoke($block->module, 'block', 'list'); - if ($data[$block->delta]['info']) { - $form .= form_checkbox($data[$block->delta]['info'], "block][$block->module][$block->delta", 1, isset($user->block[$block->module][$block->delta]) ? $user->block[$block->module][$block->delta] : $block->status); + while ($block = db_fetch_object($result)) { + $data = module_invoke($block->module, 'block', 'list'); + if ($data[$block->delta]['info']) { + $form .= form_checkbox($data[$block->delta]['info'], "block][$block->module][$block->delta", 1, isset($user->block[$block->module][$block->delta]) ? $user->block[$block->module][$block->delta] : $block->status); + } } - } - if (isset($form)) { - return array(t('Block configuration') => $form); + if (isset($form)) { + return array(array('title' => t('Block configuration'), 'data' => $form, 'weight' => 2)); + } } break; diff --git a/modules/locale.module b/modules/locale.module index d5a3a0f13fe..48e2ae5284e 100644 --- a/modules/locale.module +++ b/modules/locale.module @@ -92,10 +92,13 @@ function locale_menu() { /** * Implementation of hook_user(). Allows each user to select an interface language. */ -function locale_user($type, &$edit, &$user) { +function locale_user($type, &$edit, &$user, $category = NULL) { global $languages; - if ($type == 'form' && count($languages) > 1) { - return array(t('Locale settings') => form_radios(t('Language'), 'language', $user->language, $languages, t('Selecting a different language will change the language of the site.'))); + if ($type == 'form' && count($languages) > 1 && $category == 'account') { + return array(array( + 'title' => t('Locale settings'), + 'data' => form_radios(t('Language'), 'language', $user->language, $languages, t('Selecting a different language will change the language of the site.')), + 'weight' => 2)); } } diff --git a/modules/locale/locale.module b/modules/locale/locale.module index d5a3a0f13fe..48e2ae5284e 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -92,10 +92,13 @@ function locale_menu() { /** * Implementation of hook_user(). Allows each user to select an interface language. */ -function locale_user($type, &$edit, &$user) { +function locale_user($type, &$edit, &$user, $category = NULL) { global $languages; - if ($type == 'form' && count($languages) > 1) { - return array(t('Locale settings') => form_radios(t('Language'), 'language', $user->language, $languages, t('Selecting a different language will change the language of the site.'))); + if ($type == 'form' && count($languages) > 1 && $category == 'account') { + return array(array( + 'title' => t('Locale settings'), + 'data' => form_radios(t('Language'), 'language', $user->language, $languages, t('Selecting a different language will change the language of the site.')), + 'weight' => 2)); } } diff --git a/modules/profile.module b/modules/profile.module index 6270b39a3c5..0a2bc98b33a 100644 --- a/modules/profile.module +++ b/modules/profile.module @@ -3,6 +3,13 @@ // TODO: add a 'date' field so we can migrate the birthday information. +/** + * Flags to define the visibility of a profile field. + */ +define('PROFILE_PRIVATE', 1); +define('PROFILE_PUBLIC', 2); +define('PROFILE_PUBLIC_LISTINGS', 3); + /** * Implementation of hook_help(). */ @@ -17,6 +24,8 @@ function profile_help($section) { * Implementation of hook_menu(). */ function profile_menu() { + global $user; + $items = array(); $items[] = array('path' => 'profile', 'title' => t('browse'), 'callback' => 'profile_browse', @@ -38,6 +47,7 @@ function profile_menu() { 'callback' => 'profile_admin_delete', 'access' => user_access('administer users'), 'type' => MENU_CALLBACK); + return $items; } @@ -54,7 +64,7 @@ function profile_browse() { if ($field->fid) { // Compile a list of fields to show $fields = array(); - $result = db_query('SELECT name, title, type FROM {profile_fields} WHERE fid != %d AND overview = 1', $field->fid); + $result = db_query('SELECT name, title, type FROM {profile_fields} WHERE fid != %d AND visibility = %d', $field->fid, PROFILE_PUBLIC_LISTINGS); while ($record = db_fetch_object($result)) { $fields[] = $record; } @@ -105,14 +115,12 @@ function profile_load_profile(&$user) { } } -function profile_save_profile(&$edit, &$user) { - db_query('DELETE FROM {profile_values} WHERE uid = %d', $user->uid); - $result = db_query('SELECT fid, name FROM {profile_fields}'); +function profile_save_profile(&$edit, &$user, $category) { + $result = db_query("SELECT fid, name FROM {profile_fields} WHERE LOWER(category) = '%s'", strtolower($category)); while ($field = db_fetch_object($result)) { - if ($edit[$field->name]) { - 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}); - } + 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}); } } @@ -127,7 +135,8 @@ function profile_view_field($user, $field) { case 'checkbox': return l($field->title, "profile/$field->name"); case 'url': - return ''. strip_tags($value) .''; case 'list': + return ''. strip_tags($value) .''; + case 'list': $values = split("[\n\r]", $value); $fields = array(); foreach ($values as $value) { @@ -144,7 +153,7 @@ function profile_view_profile($user) { profile_load_profile($user); - $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + $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') { @@ -159,24 +168,42 @@ function profile_view_profile($user) { return $fields; } -function profile_edit_profile($edit, $user) { +function _profile_form_explanation($field) { + $output = $field->explanation; - $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + if ($field->type == 'list') { + $output .= ' '. t('Put each item on a separate line. No HTML allowed.'); + } + + if ($field->required) { + $output .= ' '. t('This is a required field.'); + } + + 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) { + + $result = db_query("SELECT * FROM {profile_fields} WHERE LOWER(category) = '%s' ORDER BY weight", strtolower($category)); while ($field = db_fetch_object($result)) { switch ($field->type) { case 'textfield': case 'url': - $fields[$field->category] .= form_textfield($field->title, $field->name, $edit[$field->name], 70, 255, $field->explanation, NULL, $field->required); + $output .= form_textfield($field->title, $field->name, $edit[$field->name], 70, 255, _profile_form_explanation($field), NULL, $field->required); break; case 'textarea': - $fields[$field->category] .= form_textarea($field->title, $field->name, $edit[$field->name], 60, 5, $field->explanation, NULL, $field->required); + $output .= form_textarea($field->title, $field->name, $edit[$field->name], 60, 5, _profile_form_explanation($field), NULL, $field->required); break; case 'list': - $fields[$field->category] .= form_textarea($field->title, $field->name, $edit[$field->name], 60, 5, $field->explanation .' '. t('Put each item on a separate line. No HTML allowed.'), NULL, $field->required); + $output .= form_textarea($field->title, $field->name, $edit[$field->name], 60, 5, _profile_form_explanation($field), NULL, $field->required); break; case 'checkbox': - $fields[$field->category] .= form_checkbox($field->title, $field->name, 1, $edit[$field->name], $field->explanation, NULL, $field->required); + $output .= form_checkbox($field->title, $field->name, 1, $edit[$field->name], _profile_form_explanation($field), NULL, $field->required); break; case 'selection': $options = array('--'); @@ -187,16 +214,18 @@ function profile_edit_profile($edit, $user) { } } - $fields[$field->category] .= form_select($field->title, $field->name, $edit[$field->name], $options, $field->explanation, 0, 0, $field->required); + $output .= form_select($field->title, $field->name, $edit[$field->name], $options, _profile_form_explanation($field), 0, 0, $field->required); break; } } - return $fields; + if ($output) { + return array(array('title' => $category, 'data' => $output)); + } } -function profile_validate_profile($edit) { - $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); +function profile_validate_profile($edit, $category) { + $result = db_query("SELECT * FROM {profile_fields} WHERE LOWER(category) = '%s' ORDER BY weight", strtolower($category)); while ($field = db_fetch_object($result)) { if ($edit[$field->name]) { @@ -204,7 +233,7 @@ function profile_validate_profile($edit) { form_set_error($field->name, t('The value provided for "%field" is not a valid URL.', array('%field' => $field->title))); } } - else if ($field->required) { + else if ($field->required && !user_access('administer users')) { form_set_error($field->name, t('The field "%field" is required.', array('%field' => $field->title))); } } @@ -212,22 +241,32 @@ function profile_validate_profile($edit) { return $edit; } +function profile_categories() { + $result = db_query("SELECT DISTINCT(category) FROM {profile_fields}"); + while ($category = db_fetch_object($result)) { + $data[] = array('name' => htmlentities(strtolower($category->category)), 'title' => strtolower($category->category), 'weight' => 3); + } + return $data; +} + /** * Implementation of hook_user(). */ -function profile_user($type, &$edit, &$user) { +function profile_user($type, &$edit, &$user, $category = NULL) { switch ($type) { case 'load': return profile_load_profile($user); case 'update': case 'insert': - return profile_save_profile($edit, $user); + return profile_save_profile($edit, $user, $category); case 'view': return profile_view_profile($user); case 'form': - return profile_edit_profile($edit, $user); + return profile_form_profile($edit, $user, $category); case 'validate': - return profile_validate_profile($edit); + return profile_validate_profile($edit, $category); + case 'categories': + return profile_categories(); } } @@ -277,7 +316,7 @@ function profile_admin_add($type) { } if (!form_has_errors()) { - db_query("INSERT INTO {profile_fields} (title, name, explanation, category, type, weight, required, overview, 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['overview'], $data['options'], $data['page']); + 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_goto('admin/user/configure/profile'); @@ -302,7 +341,7 @@ function profile_admin_edit($fid) { profile_validate_form($data); if (!form_has_errors()) { - db_query("UPDATE {profile_fields} SET title = '%s', name = '%s', explanation = '%s', category = '%s', weight = %d, required = %d, overview = %d, options = '%s', page = '%s' WHERE fid = %d", $data['title'], $data['name'], $data['explanation'], $data['category'], $data['weight'], $data['required'], $data['overview'], $data['options'], $data['page'], $fid); + 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_goto('admin/user/configure/profile'); @@ -335,19 +374,16 @@ Unless you know what you are doing, it is highly recommended that you prefix the $group .= form_textarea(t('Selection options'), 'options', $edit['options'], 70, 8, t('A list of all options. Put each option on a separate line. Example options are "red", "blue", "green", etc.')); } $group .= form_weight(t('Weight'), 'weight', $edit['weight'], 5, t('The weights define the order in which the form fields are shown. Lighter fields "float up" towards the top of the category.')); - $group .= form_checkbox(t('Required field.'), 'required', 1, $edit['required']); - $output = form_group(t('Field settings'), $group); - - $group = ''; + $group .= form_radios(t('Visibility'), 'visibility', $edit['visibility'], array(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.'))); if ($type == 'selection' || $type == 'list') { - $group .= form_textfield(t('Page title'), 'page', $edit['page'], 70, 128, t('The title of the page showing all users with the specified field. The word %value will be substituted with the corresponding value. An example page title is "People whose favorite color is %value".')); + $group .= form_textfield(t('Page title'), 'page', $edit['page'], 70, 128, t('The title of the page showing all users with the specified field. The word %value 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.')); } else { - $group .= form_textfield(t('Page title'), 'page', $edit['page'], 70, 128, t('The title of the page showing all users with the specified field.')); + $group .= form_textfield(t('Page title'), 'page', $edit['page'], 70, 128, 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.')); } - $group .= form_checkbox(t('Should this field be shown on the member listing pages.'), 'overview', 1, $edit['overview']); + $group .= form_checkbox(t('Required field.'), 'required', 1, $edit['required']); - $output .= form_group(t('Browsability'), $group); + $output = form_group(t('Field settings'), $group); $output .= form_submit(t('Save field')); return form($output); diff --git a/modules/profile/profile.module b/modules/profile/profile.module index 6270b39a3c5..0a2bc98b33a 100644 --- a/modules/profile/profile.module +++ b/modules/profile/profile.module @@ -3,6 +3,13 @@ // TODO: add a 'date' field so we can migrate the birthday information. +/** + * Flags to define the visibility of a profile field. + */ +define('PROFILE_PRIVATE', 1); +define('PROFILE_PUBLIC', 2); +define('PROFILE_PUBLIC_LISTINGS', 3); + /** * Implementation of hook_help(). */ @@ -17,6 +24,8 @@ function profile_help($section) { * Implementation of hook_menu(). */ function profile_menu() { + global $user; + $items = array(); $items[] = array('path' => 'profile', 'title' => t('browse'), 'callback' => 'profile_browse', @@ -38,6 +47,7 @@ function profile_menu() { 'callback' => 'profile_admin_delete', 'access' => user_access('administer users'), 'type' => MENU_CALLBACK); + return $items; } @@ -54,7 +64,7 @@ function profile_browse() { if ($field->fid) { // Compile a list of fields to show $fields = array(); - $result = db_query('SELECT name, title, type FROM {profile_fields} WHERE fid != %d AND overview = 1', $field->fid); + $result = db_query('SELECT name, title, type FROM {profile_fields} WHERE fid != %d AND visibility = %d', $field->fid, PROFILE_PUBLIC_LISTINGS); while ($record = db_fetch_object($result)) { $fields[] = $record; } @@ -105,14 +115,12 @@ function profile_load_profile(&$user) { } } -function profile_save_profile(&$edit, &$user) { - db_query('DELETE FROM {profile_values} WHERE uid = %d', $user->uid); - $result = db_query('SELECT fid, name FROM {profile_fields}'); +function profile_save_profile(&$edit, &$user, $category) { + $result = db_query("SELECT fid, name FROM {profile_fields} WHERE LOWER(category) = '%s'", strtolower($category)); while ($field = db_fetch_object($result)) { - if ($edit[$field->name]) { - 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}); - } + 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}); } } @@ -127,7 +135,8 @@ function profile_view_field($user, $field) { case 'checkbox': return l($field->title, "profile/$field->name"); case 'url': - return ''. strip_tags($value) .''; case 'list': + return ''. strip_tags($value) .''; + case 'list': $values = split("[\n\r]", $value); $fields = array(); foreach ($values as $value) { @@ -144,7 +153,7 @@ function profile_view_profile($user) { profile_load_profile($user); - $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + $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') { @@ -159,24 +168,42 @@ function profile_view_profile($user) { return $fields; } -function profile_edit_profile($edit, $user) { +function _profile_form_explanation($field) { + $output = $field->explanation; - $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + if ($field->type == 'list') { + $output .= ' '. t('Put each item on a separate line. No HTML allowed.'); + } + + if ($field->required) { + $output .= ' '. t('This is a required field.'); + } + + 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) { + + $result = db_query("SELECT * FROM {profile_fields} WHERE LOWER(category) = '%s' ORDER BY weight", strtolower($category)); while ($field = db_fetch_object($result)) { switch ($field->type) { case 'textfield': case 'url': - $fields[$field->category] .= form_textfield($field->title, $field->name, $edit[$field->name], 70, 255, $field->explanation, NULL, $field->required); + $output .= form_textfield($field->title, $field->name, $edit[$field->name], 70, 255, _profile_form_explanation($field), NULL, $field->required); break; case 'textarea': - $fields[$field->category] .= form_textarea($field->title, $field->name, $edit[$field->name], 60, 5, $field->explanation, NULL, $field->required); + $output .= form_textarea($field->title, $field->name, $edit[$field->name], 60, 5, _profile_form_explanation($field), NULL, $field->required); break; case 'list': - $fields[$field->category] .= form_textarea($field->title, $field->name, $edit[$field->name], 60, 5, $field->explanation .' '. t('Put each item on a separate line. No HTML allowed.'), NULL, $field->required); + $output .= form_textarea($field->title, $field->name, $edit[$field->name], 60, 5, _profile_form_explanation($field), NULL, $field->required); break; case 'checkbox': - $fields[$field->category] .= form_checkbox($field->title, $field->name, 1, $edit[$field->name], $field->explanation, NULL, $field->required); + $output .= form_checkbox($field->title, $field->name, 1, $edit[$field->name], _profile_form_explanation($field), NULL, $field->required); break; case 'selection': $options = array('--'); @@ -187,16 +214,18 @@ function profile_edit_profile($edit, $user) { } } - $fields[$field->category] .= form_select($field->title, $field->name, $edit[$field->name], $options, $field->explanation, 0, 0, $field->required); + $output .= form_select($field->title, $field->name, $edit[$field->name], $options, _profile_form_explanation($field), 0, 0, $field->required); break; } } - return $fields; + if ($output) { + return array(array('title' => $category, 'data' => $output)); + } } -function profile_validate_profile($edit) { - $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); +function profile_validate_profile($edit, $category) { + $result = db_query("SELECT * FROM {profile_fields} WHERE LOWER(category) = '%s' ORDER BY weight", strtolower($category)); while ($field = db_fetch_object($result)) { if ($edit[$field->name]) { @@ -204,7 +233,7 @@ function profile_validate_profile($edit) { form_set_error($field->name, t('The value provided for "%field" is not a valid URL.', array('%field' => $field->title))); } } - else if ($field->required) { + else if ($field->required && !user_access('administer users')) { form_set_error($field->name, t('The field "%field" is required.', array('%field' => $field->title))); } } @@ -212,22 +241,32 @@ function profile_validate_profile($edit) { return $edit; } +function profile_categories() { + $result = db_query("SELECT DISTINCT(category) FROM {profile_fields}"); + while ($category = db_fetch_object($result)) { + $data[] = array('name' => htmlentities(strtolower($category->category)), 'title' => strtolower($category->category), 'weight' => 3); + } + return $data; +} + /** * Implementation of hook_user(). */ -function profile_user($type, &$edit, &$user) { +function profile_user($type, &$edit, &$user, $category = NULL) { switch ($type) { case 'load': return profile_load_profile($user); case 'update': case 'insert': - return profile_save_profile($edit, $user); + return profile_save_profile($edit, $user, $category); case 'view': return profile_view_profile($user); case 'form': - return profile_edit_profile($edit, $user); + return profile_form_profile($edit, $user, $category); case 'validate': - return profile_validate_profile($edit); + return profile_validate_profile($edit, $category); + case 'categories': + return profile_categories(); } } @@ -277,7 +316,7 @@ function profile_admin_add($type) { } if (!form_has_errors()) { - db_query("INSERT INTO {profile_fields} (title, name, explanation, category, type, weight, required, overview, 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['overview'], $data['options'], $data['page']); + 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_goto('admin/user/configure/profile'); @@ -302,7 +341,7 @@ function profile_admin_edit($fid) { profile_validate_form($data); if (!form_has_errors()) { - db_query("UPDATE {profile_fields} SET title = '%s', name = '%s', explanation = '%s', category = '%s', weight = %d, required = %d, overview = %d, options = '%s', page = '%s' WHERE fid = %d", $data['title'], $data['name'], $data['explanation'], $data['category'], $data['weight'], $data['required'], $data['overview'], $data['options'], $data['page'], $fid); + 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_goto('admin/user/configure/profile'); @@ -335,19 +374,16 @@ Unless you know what you are doing, it is highly recommended that you prefix the $group .= form_textarea(t('Selection options'), 'options', $edit['options'], 70, 8, t('A list of all options. Put each option on a separate line. Example options are "red", "blue", "green", etc.')); } $group .= form_weight(t('Weight'), 'weight', $edit['weight'], 5, t('The weights define the order in which the form fields are shown. Lighter fields "float up" towards the top of the category.')); - $group .= form_checkbox(t('Required field.'), 'required', 1, $edit['required']); - $output = form_group(t('Field settings'), $group); - - $group = ''; + $group .= form_radios(t('Visibility'), 'visibility', $edit['visibility'], array(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.'))); if ($type == 'selection' || $type == 'list') { - $group .= form_textfield(t('Page title'), 'page', $edit['page'], 70, 128, t('The title of the page showing all users with the specified field. The word %value will be substituted with the corresponding value. An example page title is "People whose favorite color is %value".')); + $group .= form_textfield(t('Page title'), 'page', $edit['page'], 70, 128, t('The title of the page showing all users with the specified field. The word %value 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.')); } else { - $group .= form_textfield(t('Page title'), 'page', $edit['page'], 70, 128, t('The title of the page showing all users with the specified field.')); + $group .= form_textfield(t('Page title'), 'page', $edit['page'], 70, 128, 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.')); } - $group .= form_checkbox(t('Should this field be shown on the member listing pages.'), 'overview', 1, $edit['overview']); + $group .= form_checkbox(t('Required field.'), 'required', 1, $edit['required']); - $output .= form_group(t('Browsability'), $group); + $output = form_group(t('Field settings'), $group); $output .= form_submit(t('Save field')); return form($output); diff --git a/modules/system.module b/modules/system.module index e010c27bdc8..5fccde31e6d 100644 --- a/modules/system.module +++ b/modules/system.module @@ -94,14 +94,14 @@ function system_menu() { * * Allows users to individually set their theme and time zone. */ -function system_user($type, $edit, &$user) { - if ($type == 'form') { +function system_user($type, $edit, &$user, $category = NULL) { + if ($type == 'form' && $category == 'account') { $options = '\n"; if (count($themes = list_themes()) > 1) { foreach ($themes as $key => $value) { $options .= "\n"; } - $data[t('Theme settings')] = form_item(t('Theme'), "", t('Selecting a different theme will change the look and feel of the site.')); + $data[] = array('title' => t('Theme settings'), 'data' => form_item(t('Theme'), "", t('Selecting a different theme will change the look and feel of the site.')), 'weight' => 2); } if (!variable_get('sitewide_timezone', 0)) { @@ -111,7 +111,7 @@ function system_user($type, $edit, &$user) { $zone = $offset * 3600; $zones[$zone] = format_date($timestamp, 'custom', variable_get('date_format_long', 'l, F j, Y - H:i') . ' O', $zone); } - $data[t('Locale settings')] = form_select(t('Time zone'), 'timezone', $edit['timezone'], $zones, t('Select what time you currently have and your time zone settings will be set appropriately.')); + $data[] = array('title' => t('Locale settings'), 'data' => form_select(t('Time zone'), 'timezone', $edit['timezone'], $zones, t('Select what time you currently have and your time zone settings will be set appropriately.')), 'weight' => 2); } return $data; } diff --git a/modules/system/system.module b/modules/system/system.module index e010c27bdc8..5fccde31e6d 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -94,14 +94,14 @@ function system_menu() { * * Allows users to individually set their theme and time zone. */ -function system_user($type, $edit, &$user) { - if ($type == 'form') { +function system_user($type, $edit, &$user, $category = NULL) { + if ($type == 'form' && $category == 'account') { $options = '\n"; if (count($themes = list_themes()) > 1) { foreach ($themes as $key => $value) { $options .= "\n"; } - $data[t('Theme settings')] = form_item(t('Theme'), "", t('Selecting a different theme will change the look and feel of the site.')); + $data[] = array('title' => t('Theme settings'), 'data' => form_item(t('Theme'), "", t('Selecting a different theme will change the look and feel of the site.')), 'weight' => 2); } if (!variable_get('sitewide_timezone', 0)) { @@ -111,7 +111,7 @@ function system_user($type, $edit, &$user) { $zone = $offset * 3600; $zones[$zone] = format_date($timestamp, 'custom', variable_get('date_format_long', 'l, F j, Y - H:i') . ' O', $zone); } - $data[t('Locale settings')] = form_select(t('Time zone'), 'timezone', $edit['timezone'], $zones, t('Select what time you currently have and your time zone settings will be set appropriately.')); + $data[] = array('title' => t('Locale settings'), 'data' => form_select(t('Time zone'), 'timezone', $edit['timezone'], $zones, t('Select what time you currently have and your time zone settings will be set appropriately.')), 'weight' => 2); } return $data; } diff --git a/modules/user.module b/modules/user.module index e8b18a02973..c7fbe548c3a 100644 --- a/modules/user.module +++ b/modules/user.module @@ -7,10 +7,10 @@ * We cannot use module_invoke() for this, becuse the arguments need to * be passed by reference. */ -function user_module_invoke($type, &$array, &$user) { +function user_module_invoke($type, &$array, &$user, $category = NULL) { foreach (module_list() as $module) { $function = $module .'_user'; - if (function_exists($function)) $function($type, $array, $user); + if (function_exists($function)) $function($type, $array, $user, $category); } } @@ -62,11 +62,11 @@ function user_load($array = array()) { return $user; } -function user_save($account, $array = array()) { +function user_save($account, $array = array(), $category = 'account') { // Dynamically compose a SQL query: $user_fields = user_fields(); if ($account->uid) { - user_module_invoke('update', $array, $account); + user_module_invoke('update', $array, $account, $category); $data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid))); foreach ($array as $key => $value) { @@ -92,10 +92,10 @@ function user_save($account, $array = array()) { db_query("UPDATE {users} SET $query changed = %d WHERE uid = %d", array_merge($v, array(time(), $account->uid))); // reload user roles if provided - if (is_array($array['rid'])) { + if (is_array($array['roles'])) { db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid); - foreach ($array['rid'] as $rid) { + foreach ($array['roles'] as $rid) { db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid); } } @@ -134,13 +134,13 @@ function user_save($account, $array = array()) { // Reload user roles (delete just to be safe). db_query('DELETE FROM {users_roles} WHERE uid = %d', $array['uid']); - foreach ($array['rid'] as $rid) { + foreach ($array['roles'] as $rid) { db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $array['uid'], $rid); } $user = user_load(array('name' => $array['name'])); - module_invoke_all('user', 'insert', $array, $user); + module_invoke_all('user', 'insert', $array, $user, $category); } foreach ($array as $key => $value) { @@ -418,10 +418,22 @@ function user_search($keys) { /** * Implementation of hook_user(). */ -function user_user($type, &$edit, &$user) { +function user_user($type, &$edit, &$user, $category = NULL) { if ($type == 'view') { return array(t('History') => form_item(t('Member for'), format_interval(time() - $user->created))); } + + if ($type == 'form' && $category == 'account') { + return user_edit_form(arg(1), $edit); + } + + if ($type == 'validate' && $category == 'account') { + return user_edit_validate(arg(1), $edit); + } + + if ($type == 'categories') { + return array(array('name' => 'account', 'title' => t('account settings'), 'weight' => 1)); + } } /** @@ -595,12 +607,19 @@ function user_menu() { if (arg(0) == 'user' && is_numeric(arg(1))) { $items[] = array('path' => 'user/'. arg(1), 'title' => t('user'), 'callback' => 'user_page', 'access' => TRUE); - // Add the edit menu: - if ($access) $function = 'user_admin_edit'; - else $function = 'user_page'; $items[] = array('path' => 'user/'. arg(1) .'/edit', 'title' => t('edit'), - 'callback' => $function, 'access' => $access || $user->uid == arg(1), + 'callback' => 'user_edit', 'access' => $access || $user->uid == arg(1), 'type' => MENU_LOCAL_TASK); + + if (arg(2) == 'edit') { + if (($categories = _user_categories()) && (count($categories) > 1)) { + foreach ($categories as $key => $category) { + $items[] = array('path' => 'user/'. arg(1) .'/edit/'. $category['name'], 'title' => $category['title'], + 'callback' => $function, 'access' => $access || $user->uid == arg(1), + 'type' => MENU_LOCAL_SUBTASK, 'weight' => $category['weight']); + } + } + } } if ($user->uid) { @@ -763,7 +782,7 @@ function user_login($edit = array(), $msg = '') { if (module_hook($module, 'auth')) { if (module_invoke($module, 'auth', $name, $pass, $server)) { if (variable_get('user_register', 1) == 1 && !user_load(array('name' => "$name@$server"))) { // Register this new user. - $user = user_save('', array('name' => "$name@$server", 'pass' => user_password(), 'init' => "$name@$server", 'status' => 1, "authname_$module" => "$name@$server", 'rid' => array(_user_authenticated_id()))); + $user = user_save('', array('name' => "$name@$server", 'pass' => user_password(), 'init' => "$name@$server", 'status' => 1, "authname_$module" => "$name@$server", 'roles' => array(_user_authenticated_id()))); watchdog('user', "new user: $name@$server ($module ID)", l(t('edit user'), "user/$user->uid/edit")); break; } @@ -919,34 +938,7 @@ function user_register($edit = array()) { } if ($edit) { - if ($error = user_validate_name($edit['name'])) { - form_set_error('name', $error); - } - else if ($error = user_validate_mail($edit['mail'])) { - form_set_error('mail', $error); - } - else if (user_deny('user', $edit['name'])) { - form_set_error('name', t('The name "%s" has been denied access.', array('%s' => $edit['name']))); - } - else if (user_deny('mail', $edit['mail'])) { - form_set_error('mail', t('The e-mail address "%s" has been denied access.', array('%s' => $edit['mail']))); - } - else if (db_num_rows(db_query("SELECT name FROM {users} WHERE LOWER(name) = LOWER('%s')", $edit['name'])) > 0) { - form_set_error('name', t('The name "%s" is already taken.', array('%s' => $edit['name']))); - } - else if (db_num_rows(db_query("SELECT mail FROM {users} WHERE LOWER(mail) = LOWER('%s') OR LOWER(init) = LOWER('%s')", $edit['mail'], $edit['mail'])) > 0) { - form_set_error('mail', t('The e-mail address "%s" is already taken.', array('%s' => $edit['mail']))); - } - else { - foreach (module_list() as $module) { - if (module_hook($module, 'user')) { - $result = module_invoke($module, 'user', 'validate', $edit, $user); - if (is_array($result)) { - $data = array_merge($data, $result); - } - } - } - } + _user_profile($edit, NULL, 'validate', 'account'); if (!form_has_errors()) { $from = variable_get('site_mail', ini_get('sendmail_from')); @@ -954,7 +946,7 @@ function user_register($edit = array()) { // TODO: Is this necessary? Won't session_write() replicate this? unset($edit['session']); - $account = user_save('', array_merge(array('name' => $edit['name'], 'pass' => $pass, 'init' => $edit['mail'], 'mail' => $edit['mail'], 'rid' => array(_user_authenticated_id()), 'status' => (variable_get('user_register', 1) == 1 ? 1 : 0)), $data)); + $account = user_save('', array_merge(array('name' => $edit['name'], 'pass' => $pass, 'init' => $edit['mail'], 'mail' => $edit['mail'], 'roles' => array(_user_authenticated_id()), 'status' => (variable_get('user_register', 1) == 1 ? 1 : 0)), $data)); watchdog('user', 'new user: "'. $edit['name'] .'" <'. $edit['mail'] .'>', l(t('edit user'), "admin/user/edit/$account->uid")); $variables = array('%username' => $edit['name'], '%site' => variable_get('site_name', 'drupal'), '%password' => $pass, '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $edit['mail'], '%date' => format_date(time()), '%login_uri' => url('user/login', NULL, NULL, TRUE), '%edit_uri' => url('user/edit', NULL, NULL, TRUE)); @@ -1000,7 +992,6 @@ function user_register($edit = array()) { } $output .= form_textfield(t('Username'), 'name', $edit['name'], 30, 64, t('Your full name or your preferred username; only letters, numbers and spaces are allowed.')); $output .= form_textfield(t('E-mail address'), 'mail', $edit['mail'], 30, 64, t('A password and instructions will be sent to this e-mail address, so make sure it is accurate.')); - $output .= _user_profile($edit, $edit); $output .= form_submit(t('Create new account')); $items[] = l(t('Request new password'), 'user/password'); $items[] = l(t('Log in'), 'user/login'); @@ -1010,120 +1001,118 @@ function user_register($edit = array()) { return form($output); } -function user_edit($edit = array()) { - global $user; +function user_edit_form($uid, $edit) { + // Account information: + $group = form_textfield(t('Username'), 'name', $edit['name'], 30, 55, t('Your full name or your preferred username: only letters, numbers and spaces are allowed.')); + $group .= form_textfield(t('E-mail address'), 'mail', $edit['mail'], 30, 55, t('Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.')); + $group .= form_item(t('Password'), ' ', t('Enter your new password twice if you want to change your current password, or leave it blank if you are happy with your current password.')); - if ($user->uid) { - if (!(is_null($edit['name']) && is_null($edit['mail']))) { - if ($error = user_validate_name($edit['name'])) { - form_set_error('name', $error); - } - else if ($error = user_validate_mail($edit['mail'])) { - form_set_error('mail', $error); - } - else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != $user->uid AND LOWER(name) = LOWER('%s')", $edit['name'])) > 0) { - form_set_error('name', t('The name "%s" is already taken.', array('%s' => $edit['name']))); - } - else if ($edit['mail'] && db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != $user->uid AND LOWER(mail) = LOWER('%s')", $edit['mail'])) > 0) { - form_set_name('mail', t('The e-mail address "%s" is already taken.', array('%s' => $edit['mail']))); - } - else { - // If required, validate the picture. - if ($file = file_check_upload('picture')) { - user_validate_picture($file, $edit, $user); - } - - // If required, check that proposed passwords match. If so, - // add new password to $edit. - if ($edit['pass1']) { - if ($edit['pass1'] == $edit['pass2']) { - $edit['pass'] = $edit['pass1']; - } - else { - form_set_error('pass2', t('The specified passwords do not match.')); - } - } - unset($edit['pass1'], $edit['pass2']); - - // Validate input fields to make sure users don't submit - // invalid form data. - if (!user_access('administer users')) { - if (array_intersect(array_keys($edit), array('rid', 'init', 'session'))) { - watchdog('warning', 'detected malicious attempt to alter a protected database field'); - } - - $edit['rid'] = array_keys($user->roles); - $edit['init'] = $user->init; - $edit['session'] = $user->session; - } - - // Have the modules that extend the user information validate - // their data. - foreach (module_list() as $module) { - if (module_hook($module, 'user')) { - $result = module_invoke($module, 'user', 'validate', $edit, $user); - } - if (is_array($result)) { - $data = array_merge($data, $result); - } - } - - if (!form_has_errors()) { - // Save user information. - $user = user_save($user, array_merge($edit, $data)); - - drupal_set_message(t('your user information changes have been saved.')); - } - } - } - - if (!$edit) { - $edit = object2array($user); - } - - $group = form_textfield(t('Username'), 'name', $edit['name'], 30, 55, t('Your full name or your preferred username: only letters, numbers and spaces are allowed.')); - $group .= form_textfield(t('E-mail address'), 'mail', $edit['mail'], 30, 55, t('Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.')); - $group .= form_item(t('Password'), ' ', t('Enter your new password twice if you want to change your current password, or leave it blank if you are happy with your current password.')); - $output = form_group(t('Account information'), $group); - - if (variable_get('user_pictures', 0)) { - $group = ''; - if (file_exists($user->picture)) { - $group .= ''; - } - $group .= form_file(t('Upload picture'), 'picture', 48, t('Your virtual face or picture. Maximum dimensions are %dimensions and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30'))) .' '. variable_get('user_picture_guidelines', '')); - $output .= form_group(t('Picture'), $group); - } - - $output .= _user_profile($edit, $user); - $output .= form_submit(t('Save user information')); - - $output = form($output, 'post', 0, array('enctype' => 'multipart/form-data')); - // The "enctype" attribute is required to upload files such as pictures. - } - else { - $output = user_login(); + if (user_access('administer users')) { + $group .= form_radios(t('Status'), 'status', $edit['status'], array(t('Blocked'), t('Active'))); + $group .= form_checkboxes(t('Roles'), 'roles', array_keys($edit['roles']), user_roles(1), t('Select at least one role. The user receives the combined permissions of all of the selected roles.')); } - return $output; + $data[] = array('title' => t('Account information'), 'data' => $group, 'weight' => 0); + + // Picture/avatar: + if (variable_get('user_pictures', 0)) { + $group = ''; + if (file_exists($edit['picture'])) { + $group .= ''; + } + $group .= form_file(t('Upload picture'), 'picture', 48, t('Your virtual face or picture. Maximum dimensions are %dimensions and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30'))) .' '. variable_get('user_picture_guidelines', '')); + $data[] = array('title' => t('Picture'), 'data' => $group, 'weight' => 1); + } + + return $data; } -function _user_profile($edit, $account, $mode = 'form') { - $groups = array(); - foreach (module_list() as $module) { - if ($data = module_invoke($module, 'user', $mode, $edit, $account)) { - foreach ($data as $title => $form) { - $groups[$title] .= $form; +function user_edit_validate($uid, &$edit) { + // Validate the username: + if ($error = user_validate_name($edit['name'])) { + form_set_error('name', $error); + } + else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $uid, $edit['name'])) > 0) { + form_set_error('name', t('The name "%s" is already taken.', array('%s' => $edit['name']))); + } + else if (user_deny('user', $edit['name'])) { + form_set_error('name', t('The name "%s" has been denied access.', array('%s' => $edit['name']))); + } + + // Validate the e-mail address: + if ($error = user_validate_mail($edit['mail'])) { + form_set_error('mail', $error); + } + else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(mail) = LOWER('%s')", $uid, $edit['mail'])) > 0) { + form_set_name('mail', t('The e-mail address "%s" is already taken.', array('%s' => $edit['mail']))); + } + else if (user_deny('mail', $edit['mail'])) { + form_set_error('mail', t('The e-mail address "%s" has been denied access.', array('%s' => $edit['mail']))); + } + + // If required, validate the uploaded picture. + if ($file = file_check_upload('picture')) { + user_validate_picture($file, $edit, $user); + } + + // If required, check that proposed passwords match. If so, add the new password to $edit. + if ($edit['pass1']) { + if ($edit['pass1'] == $edit['pass2']) { + $edit['pass'] = $edit['pass1']; + } + else { + form_set_error('pass2', t('The specified passwords do not match.')); + } + } + unset($edit['pass1'], $edit['pass2']); + + return $edit; +} + +function user_edit($category = 'account') { + global $user; + + $account = $user->uid != arg(1) ? user_load(array('uid' => arg(1))) : $user; + $edit = $_POST['op'] ? $_POST['edit'] : object2array($account); + + if ($_POST['op'] == t('Save account')) { + _user_validate($edit, $account, $category); + + if (!form_has_errors()) { + // Validate input to ensure that non-privileged users can't alter protected data. + if (!user_access('administer users') && array_intersect(array_keys($edit), array('uid', 'roles', 'init', 'session'))) { + watchdog('warning', 'detected malicious attempt to alter a protected database field'); + } + else { + user_save($account, $edit, $category); + drupal_set_message(t('the changes have been saved.')); + drupal_goto("user/$account->uid"); } } } - - $output = ''; - foreach ($groups as $title => $form) { - $output .= form_group($title, $form); + else if ($_POST['op'] == t('Delete account')) { + if ($account->status == 0) { + db_query('DELETE FROM {users} WHERE uid = %d', $account->uid); + db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid); + db_query('DELETE FROM {authmap} WHERE uid = %d', $account->uid); + drupal_set_message(t('the account has been deleted.')); + module_invoke_all('user', 'delete', $edit, $account); + print theme('page', user_admin_account()); + drupal_goto('admin/user'); + } + else { + drupal_set_message(t('failed to delete account: the account has to be blocked first.'), 'error'); + } } - return $output; + $output = _user_forms($edit, $account, $category); + $output .= form_submit(t('Save account')); + if (user_access('administer users')) { + $output .= form_submit(t('Delete account')); + } + $output = form($output, 'post', 0, array('enctype' => 'multipart/form-data')); + + print theme('page', $output, $account->name); } function user_view($uid = 0) { @@ -1197,16 +1186,9 @@ function user_page() { break; case t('Log in'): case 'login': - $output = user_login($edit); + $outpute= user_login($edit); print theme('page', $output, t('Log in')); break; - case t('Save user information'): - case 'edit': - $output = user_edit($edit); - $GLOBALS['theme'] = init_theme(); - print theme('page', $output); - break; - case t('Logout'): case 'logout': print user_logout(); break; @@ -1283,24 +1265,13 @@ function user_configure_settings() { function user_admin_create($edit = array()) { - if ($edit['name'] || $edit['mail']) { - if ($error = user_validate_name($edit['name'])) { - form_set_error('name', $error); - } - else if ($error = user_validate_mail($edit['mail'])) { - form_set_error('mail', $error); - } - else if (db_num_rows(db_query("SELECT name FROM {users} WHERE LOWER(name) = LOWER('%s')", $edit['name'])) > 0) { - form_set_error('name', t('The name "%s" is already taken.', array('%s' => $edit['name']))); - } - else if (db_num_rows(db_query("SELECT mail FROM {users} WHERE LOWER(mail) = LOWER('%s')", $edit['mail'])) > 0) { - form_set_error('mail', t('The e-mail address "%s" is already taken.', array('%s' => $edit['mail']))); - } + if ($edit) { + _user_profile($edit, NULL, 'validate', 'account'); if (!form_has_errors()) { watchdog('user', 'new user: "'. $edit['name'] .'" <'. $edit['mail'] .'>'); - user_save('', array('name' => $edit['name'], 'pass' => $edit['pass'], 'init' => $edit['mail'], 'mail' => $edit['mail'], 'rid' => array(_user_authenticated_id()), 'status' => 1)); + user_save('', array('name' => $edit['name'], 'pass' => $edit['pass'], 'init' => $edit['mail'], 'mail' => $edit['mail'], 'roles' => array(_user_authenticated_id()), 'status' => 1)); drupal_set_message(t('created a new user account. No e-mail has been sent.')); @@ -1310,7 +1281,6 @@ function user_admin_create($edit = array()) { $output = form_textfield(t('Username'), 'name', $edit['name'], 30, 55, t('Provide the username of the new account.')); $output .= form_textfield(t('E-mail address'), 'mail', $edit['mail'], 30, 55, t('Provide the e-mail address associated with the new account.')); - $output .= _user_profile($edit, $edit); $output .= form_textfield(t('Password'), 'pass', $edit['pass'], 30, 55, t('Provide a password for the new account.')); $output .= form_submit(t('Create account')); @@ -1498,116 +1468,6 @@ function user_admin_role($edit = array()) { return $output; } -function user_admin_edit() { - $op = $_POST['op']; - $edit = $_POST['edit']; - $id = arg(1); - - if ($account = user_load(array('uid' => $id))) { - if ($op == t('Save account')) { - // TODO: This display/edit/validate should be moved to a new profile - // module implementing hook_user(). - - if ($error = user_validate_name($edit['name'])) { - form_set_error('name', $error); - } - else if ($error = user_validate_mail($edit['mail'])) { - form_set_error('mail', $error); - } - else if (count($edit['rid']) < 1) { - form_set_error('rid', t('The user must have at least one role.')); - } - else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $account->uid, $edit['name'])) > 0) { - form_set_error('name', t('The name "%s" is already taken.', array('%s' => $edit['name']))); - } - else if ($edit['mail'] && db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(mail) = LOWER('%s')", $account->uid, $edit['mail'])) > 0) { - form_set_error('mail', t('The e-mail address "%s" is already taken.', array('%s' => $edit['mail']))); - } - - // Validate fields added by other modules. - foreach (module_list() as $module) { - if (module_hook($module, 'user')) { - $result = module_invoke($module, 'user', 'validate', $edit, $account); - } - if (is_array($result)) { - $data = array_merge($data, $result); - } - } - - // If required, validate the picture. - if ($file = file_check_upload('picture')) { - user_validate_picture($file, $edit, $account); - } - - // If required, check that proposed passwords match. If so, - // add new password to $edit. - if ($edit['pass1']) { - if ($edit['pass1'] == $edit['pass2']) { - $edit['pass'] = $edit['pass1']; - } - else { - form_set_error('pass2', t('The specified passwords do not match.')); - } - } - - unset($edit['pass1'], $edit['pass2']); - if (!form_has_errors()) { - $account = user_save($account, array_merge($edit, $data)); - drupal_set_message(t('the user information changes have been saved.')); - } - } - else if ($op == t('Delete account')) { - if ($edit['status'] == 0) { - db_query('DELETE FROM {users} WHERE uid = %d', $account->uid); - db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid); - db_query('DELETE FROM {authmap} WHERE uid = %d', $account->uid); - drupal_set_message(t('the account has been deleted.')); - module_invoke_all('user', 'delete', $edit, $account); - print theme('page', user_admin_account()); - return; - } - else { - drupal_set_message(t('failed to delete account: the account has to be blocked first.'), 'error'); - } - } - - if (!$edit) { - $edit = object2array($account); - } - - // Display user form: - $group = form_item(t('User ID'), $account->uid); - $group .= form_textfield(t('Username'), 'name', $account->name, 30, 55, t('Your full name or your preferred username: only letters, numbers and spaces are allowed.')); - $group .= form_textfield(t('E-mail address'), 'mail', $account->mail, 30, 55, t('Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.')); - $group .= form_item(t('Password'), ' ', t('Enter a new password twice if you want to change the current password for this user or leave it blank if you are happy with the current password.')); - $group .= form_radios(t('Status'), 'status', $account->status, array(t('Blocked'), t('Active'))); - $group .= form_checkboxes(t('Roles'), 'rid', array_keys($account->roles), user_roles(1), t('Select at least one role. The user receives the combined permissions of all of the selected roles.')); - - $output = form_group(t('Account information'), $group); - - if (variable_get('user_pictures', 0)) { - $group = ''; - if (file_exists($account->picture)) { - $group .= ''; - } - $group .= form_file(t('Upload picture or picture'), 'picture', 48, t('Maximum dimensions are %dimensions and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30')))); - $output .= form_group(t('Picture'), $group); - } - - $output .= _user_profile($edit, $account, 'form'); - - $output .= form_submit(t('Save account')); - $output .= form_submit(t('Delete account')); - - $output = form($output, 'post', 0, array('enctype' => 'multipart/form-data')); - - print theme('page', $output, $account->name); - } - else { - print theme('page', t('No such user')); - } -} - function user_admin_account() { $header = array( array('data' => t('ID'), 'field' => 'u.uid'), @@ -1780,4 +1640,56 @@ function user_help_page() { print theme('page', user_help('admin/help#user')); } +/** + * Retrieve a list of all user setting/information categories and sort them by weight. + */ +function _user_categories() { + $categories = array(); + + foreach (module_list() as $module) { + if ($data = module_invoke($module, 'user', 'categories')) { + foreach ($data as $category) { + $categories[$category['weight']] = $category; + } + } + } + + return $categories; +} + +function _user_sort($a, $b) { + return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1)); +} + +/** + * Retrieve a list of all form elements for the specified category. + */ +function _user_forms(&$edit, $account, $category) { + $groups = array(); + foreach (module_list() as $module) { + if ($data = module_invoke($module, 'user', 'form', $edit, $account, $category)) { + $groups = array_merge($data, $groups); + } + } + + usort($groups, '_user_sort'); + + $output = ''; + foreach ($groups as $group) { + $output .= form_group($group['title'], $group['data']); + } + + return $output; +} + +/** + * Validate the user data for the specified category. + */ +function _user_validate(&$edit, $account, $category) { + foreach (module_list() as $module) { + module_invoke($module, 'user', 'validate', $edit, $account, $category); + } +} + + ?> diff --git a/modules/user/user.module b/modules/user/user.module index e8b18a02973..c7fbe548c3a 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -7,10 +7,10 @@ * We cannot use module_invoke() for this, becuse the arguments need to * be passed by reference. */ -function user_module_invoke($type, &$array, &$user) { +function user_module_invoke($type, &$array, &$user, $category = NULL) { foreach (module_list() as $module) { $function = $module .'_user'; - if (function_exists($function)) $function($type, $array, $user); + if (function_exists($function)) $function($type, $array, $user, $category); } } @@ -62,11 +62,11 @@ function user_load($array = array()) { return $user; } -function user_save($account, $array = array()) { +function user_save($account, $array = array(), $category = 'account') { // Dynamically compose a SQL query: $user_fields = user_fields(); if ($account->uid) { - user_module_invoke('update', $array, $account); + user_module_invoke('update', $array, $account, $category); $data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid))); foreach ($array as $key => $value) { @@ -92,10 +92,10 @@ function user_save($account, $array = array()) { db_query("UPDATE {users} SET $query changed = %d WHERE uid = %d", array_merge($v, array(time(), $account->uid))); // reload user roles if provided - if (is_array($array['rid'])) { + if (is_array($array['roles'])) { db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid); - foreach ($array['rid'] as $rid) { + foreach ($array['roles'] as $rid) { db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid); } } @@ -134,13 +134,13 @@ function user_save($account, $array = array()) { // Reload user roles (delete just to be safe). db_query('DELETE FROM {users_roles} WHERE uid = %d', $array['uid']); - foreach ($array['rid'] as $rid) { + foreach ($array['roles'] as $rid) { db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $array['uid'], $rid); } $user = user_load(array('name' => $array['name'])); - module_invoke_all('user', 'insert', $array, $user); + module_invoke_all('user', 'insert', $array, $user, $category); } foreach ($array as $key => $value) { @@ -418,10 +418,22 @@ function user_search($keys) { /** * Implementation of hook_user(). */ -function user_user($type, &$edit, &$user) { +function user_user($type, &$edit, &$user, $category = NULL) { if ($type == 'view') { return array(t('History') => form_item(t('Member for'), format_interval(time() - $user->created))); } + + if ($type == 'form' && $category == 'account') { + return user_edit_form(arg(1), $edit); + } + + if ($type == 'validate' && $category == 'account') { + return user_edit_validate(arg(1), $edit); + } + + if ($type == 'categories') { + return array(array('name' => 'account', 'title' => t('account settings'), 'weight' => 1)); + } } /** @@ -595,12 +607,19 @@ function user_menu() { if (arg(0) == 'user' && is_numeric(arg(1))) { $items[] = array('path' => 'user/'. arg(1), 'title' => t('user'), 'callback' => 'user_page', 'access' => TRUE); - // Add the edit menu: - if ($access) $function = 'user_admin_edit'; - else $function = 'user_page'; $items[] = array('path' => 'user/'. arg(1) .'/edit', 'title' => t('edit'), - 'callback' => $function, 'access' => $access || $user->uid == arg(1), + 'callback' => 'user_edit', 'access' => $access || $user->uid == arg(1), 'type' => MENU_LOCAL_TASK); + + if (arg(2) == 'edit') { + if (($categories = _user_categories()) && (count($categories) > 1)) { + foreach ($categories as $key => $category) { + $items[] = array('path' => 'user/'. arg(1) .'/edit/'. $category['name'], 'title' => $category['title'], + 'callback' => $function, 'access' => $access || $user->uid == arg(1), + 'type' => MENU_LOCAL_SUBTASK, 'weight' => $category['weight']); + } + } + } } if ($user->uid) { @@ -763,7 +782,7 @@ function user_login($edit = array(), $msg = '') { if (module_hook($module, 'auth')) { if (module_invoke($module, 'auth', $name, $pass, $server)) { if (variable_get('user_register', 1) == 1 && !user_load(array('name' => "$name@$server"))) { // Register this new user. - $user = user_save('', array('name' => "$name@$server", 'pass' => user_password(), 'init' => "$name@$server", 'status' => 1, "authname_$module" => "$name@$server", 'rid' => array(_user_authenticated_id()))); + $user = user_save('', array('name' => "$name@$server", 'pass' => user_password(), 'init' => "$name@$server", 'status' => 1, "authname_$module" => "$name@$server", 'roles' => array(_user_authenticated_id()))); watchdog('user', "new user: $name@$server ($module ID)", l(t('edit user'), "user/$user->uid/edit")); break; } @@ -919,34 +938,7 @@ function user_register($edit = array()) { } if ($edit) { - if ($error = user_validate_name($edit['name'])) { - form_set_error('name', $error); - } - else if ($error = user_validate_mail($edit['mail'])) { - form_set_error('mail', $error); - } - else if (user_deny('user', $edit['name'])) { - form_set_error('name', t('The name "%s" has been denied access.', array('%s' => $edit['name']))); - } - else if (user_deny('mail', $edit['mail'])) { - form_set_error('mail', t('The e-mail address "%s" has been denied access.', array('%s' => $edit['mail']))); - } - else if (db_num_rows(db_query("SELECT name FROM {users} WHERE LOWER(name) = LOWER('%s')", $edit['name'])) > 0) { - form_set_error('name', t('The name "%s" is already taken.', array('%s' => $edit['name']))); - } - else if (db_num_rows(db_query("SELECT mail FROM {users} WHERE LOWER(mail) = LOWER('%s') OR LOWER(init) = LOWER('%s')", $edit['mail'], $edit['mail'])) > 0) { - form_set_error('mail', t('The e-mail address "%s" is already taken.', array('%s' => $edit['mail']))); - } - else { - foreach (module_list() as $module) { - if (module_hook($module, 'user')) { - $result = module_invoke($module, 'user', 'validate', $edit, $user); - if (is_array($result)) { - $data = array_merge($data, $result); - } - } - } - } + _user_profile($edit, NULL, 'validate', 'account'); if (!form_has_errors()) { $from = variable_get('site_mail', ini_get('sendmail_from')); @@ -954,7 +946,7 @@ function user_register($edit = array()) { // TODO: Is this necessary? Won't session_write() replicate this? unset($edit['session']); - $account = user_save('', array_merge(array('name' => $edit['name'], 'pass' => $pass, 'init' => $edit['mail'], 'mail' => $edit['mail'], 'rid' => array(_user_authenticated_id()), 'status' => (variable_get('user_register', 1) == 1 ? 1 : 0)), $data)); + $account = user_save('', array_merge(array('name' => $edit['name'], 'pass' => $pass, 'init' => $edit['mail'], 'mail' => $edit['mail'], 'roles' => array(_user_authenticated_id()), 'status' => (variable_get('user_register', 1) == 1 ? 1 : 0)), $data)); watchdog('user', 'new user: "'. $edit['name'] .'" <'. $edit['mail'] .'>', l(t('edit user'), "admin/user/edit/$account->uid")); $variables = array('%username' => $edit['name'], '%site' => variable_get('site_name', 'drupal'), '%password' => $pass, '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $edit['mail'], '%date' => format_date(time()), '%login_uri' => url('user/login', NULL, NULL, TRUE), '%edit_uri' => url('user/edit', NULL, NULL, TRUE)); @@ -1000,7 +992,6 @@ function user_register($edit = array()) { } $output .= form_textfield(t('Username'), 'name', $edit['name'], 30, 64, t('Your full name or your preferred username; only letters, numbers and spaces are allowed.')); $output .= form_textfield(t('E-mail address'), 'mail', $edit['mail'], 30, 64, t('A password and instructions will be sent to this e-mail address, so make sure it is accurate.')); - $output .= _user_profile($edit, $edit); $output .= form_submit(t('Create new account')); $items[] = l(t('Request new password'), 'user/password'); $items[] = l(t('Log in'), 'user/login'); @@ -1010,120 +1001,118 @@ function user_register($edit = array()) { return form($output); } -function user_edit($edit = array()) { - global $user; +function user_edit_form($uid, $edit) { + // Account information: + $group = form_textfield(t('Username'), 'name', $edit['name'], 30, 55, t('Your full name or your preferred username: only letters, numbers and spaces are allowed.')); + $group .= form_textfield(t('E-mail address'), 'mail', $edit['mail'], 30, 55, t('Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.')); + $group .= form_item(t('Password'), ' ', t('Enter your new password twice if you want to change your current password, or leave it blank if you are happy with your current password.')); - if ($user->uid) { - if (!(is_null($edit['name']) && is_null($edit['mail']))) { - if ($error = user_validate_name($edit['name'])) { - form_set_error('name', $error); - } - else if ($error = user_validate_mail($edit['mail'])) { - form_set_error('mail', $error); - } - else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != $user->uid AND LOWER(name) = LOWER('%s')", $edit['name'])) > 0) { - form_set_error('name', t('The name "%s" is already taken.', array('%s' => $edit['name']))); - } - else if ($edit['mail'] && db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != $user->uid AND LOWER(mail) = LOWER('%s')", $edit['mail'])) > 0) { - form_set_name('mail', t('The e-mail address "%s" is already taken.', array('%s' => $edit['mail']))); - } - else { - // If required, validate the picture. - if ($file = file_check_upload('picture')) { - user_validate_picture($file, $edit, $user); - } - - // If required, check that proposed passwords match. If so, - // add new password to $edit. - if ($edit['pass1']) { - if ($edit['pass1'] == $edit['pass2']) { - $edit['pass'] = $edit['pass1']; - } - else { - form_set_error('pass2', t('The specified passwords do not match.')); - } - } - unset($edit['pass1'], $edit['pass2']); - - // Validate input fields to make sure users don't submit - // invalid form data. - if (!user_access('administer users')) { - if (array_intersect(array_keys($edit), array('rid', 'init', 'session'))) { - watchdog('warning', 'detected malicious attempt to alter a protected database field'); - } - - $edit['rid'] = array_keys($user->roles); - $edit['init'] = $user->init; - $edit['session'] = $user->session; - } - - // Have the modules that extend the user information validate - // their data. - foreach (module_list() as $module) { - if (module_hook($module, 'user')) { - $result = module_invoke($module, 'user', 'validate', $edit, $user); - } - if (is_array($result)) { - $data = array_merge($data, $result); - } - } - - if (!form_has_errors()) { - // Save user information. - $user = user_save($user, array_merge($edit, $data)); - - drupal_set_message(t('your user information changes have been saved.')); - } - } - } - - if (!$edit) { - $edit = object2array($user); - } - - $group = form_textfield(t('Username'), 'name', $edit['name'], 30, 55, t('Your full name or your preferred username: only letters, numbers and spaces are allowed.')); - $group .= form_textfield(t('E-mail address'), 'mail', $edit['mail'], 30, 55, t('Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.')); - $group .= form_item(t('Password'), ' ', t('Enter your new password twice if you want to change your current password, or leave it blank if you are happy with your current password.')); - $output = form_group(t('Account information'), $group); - - if (variable_get('user_pictures', 0)) { - $group = ''; - if (file_exists($user->picture)) { - $group .= ''; - } - $group .= form_file(t('Upload picture'), 'picture', 48, t('Your virtual face or picture. Maximum dimensions are %dimensions and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30'))) .' '. variable_get('user_picture_guidelines', '')); - $output .= form_group(t('Picture'), $group); - } - - $output .= _user_profile($edit, $user); - $output .= form_submit(t('Save user information')); - - $output = form($output, 'post', 0, array('enctype' => 'multipart/form-data')); - // The "enctype" attribute is required to upload files such as pictures. - } - else { - $output = user_login(); + if (user_access('administer users')) { + $group .= form_radios(t('Status'), 'status', $edit['status'], array(t('Blocked'), t('Active'))); + $group .= form_checkboxes(t('Roles'), 'roles', array_keys($edit['roles']), user_roles(1), t('Select at least one role. The user receives the combined permissions of all of the selected roles.')); } - return $output; + $data[] = array('title' => t('Account information'), 'data' => $group, 'weight' => 0); + + // Picture/avatar: + if (variable_get('user_pictures', 0)) { + $group = ''; + if (file_exists($edit['picture'])) { + $group .= ''; + } + $group .= form_file(t('Upload picture'), 'picture', 48, t('Your virtual face or picture. Maximum dimensions are %dimensions and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30'))) .' '. variable_get('user_picture_guidelines', '')); + $data[] = array('title' => t('Picture'), 'data' => $group, 'weight' => 1); + } + + return $data; } -function _user_profile($edit, $account, $mode = 'form') { - $groups = array(); - foreach (module_list() as $module) { - if ($data = module_invoke($module, 'user', $mode, $edit, $account)) { - foreach ($data as $title => $form) { - $groups[$title] .= $form; +function user_edit_validate($uid, &$edit) { + // Validate the username: + if ($error = user_validate_name($edit['name'])) { + form_set_error('name', $error); + } + else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $uid, $edit['name'])) > 0) { + form_set_error('name', t('The name "%s" is already taken.', array('%s' => $edit['name']))); + } + else if (user_deny('user', $edit['name'])) { + form_set_error('name', t('The name "%s" has been denied access.', array('%s' => $edit['name']))); + } + + // Validate the e-mail address: + if ($error = user_validate_mail($edit['mail'])) { + form_set_error('mail', $error); + } + else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(mail) = LOWER('%s')", $uid, $edit['mail'])) > 0) { + form_set_name('mail', t('The e-mail address "%s" is already taken.', array('%s' => $edit['mail']))); + } + else if (user_deny('mail', $edit['mail'])) { + form_set_error('mail', t('The e-mail address "%s" has been denied access.', array('%s' => $edit['mail']))); + } + + // If required, validate the uploaded picture. + if ($file = file_check_upload('picture')) { + user_validate_picture($file, $edit, $user); + } + + // If required, check that proposed passwords match. If so, add the new password to $edit. + if ($edit['pass1']) { + if ($edit['pass1'] == $edit['pass2']) { + $edit['pass'] = $edit['pass1']; + } + else { + form_set_error('pass2', t('The specified passwords do not match.')); + } + } + unset($edit['pass1'], $edit['pass2']); + + return $edit; +} + +function user_edit($category = 'account') { + global $user; + + $account = $user->uid != arg(1) ? user_load(array('uid' => arg(1))) : $user; + $edit = $_POST['op'] ? $_POST['edit'] : object2array($account); + + if ($_POST['op'] == t('Save account')) { + _user_validate($edit, $account, $category); + + if (!form_has_errors()) { + // Validate input to ensure that non-privileged users can't alter protected data. + if (!user_access('administer users') && array_intersect(array_keys($edit), array('uid', 'roles', 'init', 'session'))) { + watchdog('warning', 'detected malicious attempt to alter a protected database field'); + } + else { + user_save($account, $edit, $category); + drupal_set_message(t('the changes have been saved.')); + drupal_goto("user/$account->uid"); } } } - - $output = ''; - foreach ($groups as $title => $form) { - $output .= form_group($title, $form); + else if ($_POST['op'] == t('Delete account')) { + if ($account->status == 0) { + db_query('DELETE FROM {users} WHERE uid = %d', $account->uid); + db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid); + db_query('DELETE FROM {authmap} WHERE uid = %d', $account->uid); + drupal_set_message(t('the account has been deleted.')); + module_invoke_all('user', 'delete', $edit, $account); + print theme('page', user_admin_account()); + drupal_goto('admin/user'); + } + else { + drupal_set_message(t('failed to delete account: the account has to be blocked first.'), 'error'); + } } - return $output; + $output = _user_forms($edit, $account, $category); + $output .= form_submit(t('Save account')); + if (user_access('administer users')) { + $output .= form_submit(t('Delete account')); + } + $output = form($output, 'post', 0, array('enctype' => 'multipart/form-data')); + + print theme('page', $output, $account->name); } function user_view($uid = 0) { @@ -1197,16 +1186,9 @@ function user_page() { break; case t('Log in'): case 'login': - $output = user_login($edit); + $outpute= user_login($edit); print theme('page', $output, t('Log in')); break; - case t('Save user information'): - case 'edit': - $output = user_edit($edit); - $GLOBALS['theme'] = init_theme(); - print theme('page', $output); - break; - case t('Logout'): case 'logout': print user_logout(); break; @@ -1283,24 +1265,13 @@ function user_configure_settings() { function user_admin_create($edit = array()) { - if ($edit['name'] || $edit['mail']) { - if ($error = user_validate_name($edit['name'])) { - form_set_error('name', $error); - } - else if ($error = user_validate_mail($edit['mail'])) { - form_set_error('mail', $error); - } - else if (db_num_rows(db_query("SELECT name FROM {users} WHERE LOWER(name) = LOWER('%s')", $edit['name'])) > 0) { - form_set_error('name', t('The name "%s" is already taken.', array('%s' => $edit['name']))); - } - else if (db_num_rows(db_query("SELECT mail FROM {users} WHERE LOWER(mail) = LOWER('%s')", $edit['mail'])) > 0) { - form_set_error('mail', t('The e-mail address "%s" is already taken.', array('%s' => $edit['mail']))); - } + if ($edit) { + _user_profile($edit, NULL, 'validate', 'account'); if (!form_has_errors()) { watchdog('user', 'new user: "'. $edit['name'] .'" <'. $edit['mail'] .'>'); - user_save('', array('name' => $edit['name'], 'pass' => $edit['pass'], 'init' => $edit['mail'], 'mail' => $edit['mail'], 'rid' => array(_user_authenticated_id()), 'status' => 1)); + user_save('', array('name' => $edit['name'], 'pass' => $edit['pass'], 'init' => $edit['mail'], 'mail' => $edit['mail'], 'roles' => array(_user_authenticated_id()), 'status' => 1)); drupal_set_message(t('created a new user account. No e-mail has been sent.')); @@ -1310,7 +1281,6 @@ function user_admin_create($edit = array()) { $output = form_textfield(t('Username'), 'name', $edit['name'], 30, 55, t('Provide the username of the new account.')); $output .= form_textfield(t('E-mail address'), 'mail', $edit['mail'], 30, 55, t('Provide the e-mail address associated with the new account.')); - $output .= _user_profile($edit, $edit); $output .= form_textfield(t('Password'), 'pass', $edit['pass'], 30, 55, t('Provide a password for the new account.')); $output .= form_submit(t('Create account')); @@ -1498,116 +1468,6 @@ function user_admin_role($edit = array()) { return $output; } -function user_admin_edit() { - $op = $_POST['op']; - $edit = $_POST['edit']; - $id = arg(1); - - if ($account = user_load(array('uid' => $id))) { - if ($op == t('Save account')) { - // TODO: This display/edit/validate should be moved to a new profile - // module implementing hook_user(). - - if ($error = user_validate_name($edit['name'])) { - form_set_error('name', $error); - } - else if ($error = user_validate_mail($edit['mail'])) { - form_set_error('mail', $error); - } - else if (count($edit['rid']) < 1) { - form_set_error('rid', t('The user must have at least one role.')); - } - else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $account->uid, $edit['name'])) > 0) { - form_set_error('name', t('The name "%s" is already taken.', array('%s' => $edit['name']))); - } - else if ($edit['mail'] && db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(mail) = LOWER('%s')", $account->uid, $edit['mail'])) > 0) { - form_set_error('mail', t('The e-mail address "%s" is already taken.', array('%s' => $edit['mail']))); - } - - // Validate fields added by other modules. - foreach (module_list() as $module) { - if (module_hook($module, 'user')) { - $result = module_invoke($module, 'user', 'validate', $edit, $account); - } - if (is_array($result)) { - $data = array_merge($data, $result); - } - } - - // If required, validate the picture. - if ($file = file_check_upload('picture')) { - user_validate_picture($file, $edit, $account); - } - - // If required, check that proposed passwords match. If so, - // add new password to $edit. - if ($edit['pass1']) { - if ($edit['pass1'] == $edit['pass2']) { - $edit['pass'] = $edit['pass1']; - } - else { - form_set_error('pass2', t('The specified passwords do not match.')); - } - } - - unset($edit['pass1'], $edit['pass2']); - if (!form_has_errors()) { - $account = user_save($account, array_merge($edit, $data)); - drupal_set_message(t('the user information changes have been saved.')); - } - } - else if ($op == t('Delete account')) { - if ($edit['status'] == 0) { - db_query('DELETE FROM {users} WHERE uid = %d', $account->uid); - db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid); - db_query('DELETE FROM {authmap} WHERE uid = %d', $account->uid); - drupal_set_message(t('the account has been deleted.')); - module_invoke_all('user', 'delete', $edit, $account); - print theme('page', user_admin_account()); - return; - } - else { - drupal_set_message(t('failed to delete account: the account has to be blocked first.'), 'error'); - } - } - - if (!$edit) { - $edit = object2array($account); - } - - // Display user form: - $group = form_item(t('User ID'), $account->uid); - $group .= form_textfield(t('Username'), 'name', $account->name, 30, 55, t('Your full name or your preferred username: only letters, numbers and spaces are allowed.')); - $group .= form_textfield(t('E-mail address'), 'mail', $account->mail, 30, 55, t('Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.')); - $group .= form_item(t('Password'), ' ', t('Enter a new password twice if you want to change the current password for this user or leave it blank if you are happy with the current password.')); - $group .= form_radios(t('Status'), 'status', $account->status, array(t('Blocked'), t('Active'))); - $group .= form_checkboxes(t('Roles'), 'rid', array_keys($account->roles), user_roles(1), t('Select at least one role. The user receives the combined permissions of all of the selected roles.')); - - $output = form_group(t('Account information'), $group); - - if (variable_get('user_pictures', 0)) { - $group = ''; - if (file_exists($account->picture)) { - $group .= ''; - } - $group .= form_file(t('Upload picture or picture'), 'picture', 48, t('Maximum dimensions are %dimensions and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30')))); - $output .= form_group(t('Picture'), $group); - } - - $output .= _user_profile($edit, $account, 'form'); - - $output .= form_submit(t('Save account')); - $output .= form_submit(t('Delete account')); - - $output = form($output, 'post', 0, array('enctype' => 'multipart/form-data')); - - print theme('page', $output, $account->name); - } - else { - print theme('page', t('No such user')); - } -} - function user_admin_account() { $header = array( array('data' => t('ID'), 'field' => 'u.uid'), @@ -1780,4 +1640,56 @@ function user_help_page() { print theme('page', user_help('admin/help#user')); } +/** + * Retrieve a list of all user setting/information categories and sort them by weight. + */ +function _user_categories() { + $categories = array(); + + foreach (module_list() as $module) { + if ($data = module_invoke($module, 'user', 'categories')) { + foreach ($data as $category) { + $categories[$category['weight']] = $category; + } + } + } + + return $categories; +} + +function _user_sort($a, $b) { + return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1)); +} + +/** + * Retrieve a list of all form elements for the specified category. + */ +function _user_forms(&$edit, $account, $category) { + $groups = array(); + foreach (module_list() as $module) { + if ($data = module_invoke($module, 'user', 'form', $edit, $account, $category)) { + $groups = array_merge($data, $groups); + } + } + + usort($groups, '_user_sort'); + + $output = ''; + foreach ($groups as $group) { + $output .= form_group($group['title'], $group['data']); + } + + return $output; +} + +/** + * Validate the user data for the specified category. + */ +function _user_validate(&$edit, $account, $category) { + foreach (module_list() as $module) { + module_invoke($module, 'user', 'validate', $edit, $account, $category); + } +} + + ?>