Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.382
diff -u -p -r1.382 block.module
--- modules/block/block.module	3 Oct 2009 22:08:03 -0000	1.382
+++ modules/block/block.module	4 Oct 2009 15:28:15 -0000
@@ -425,43 +425,34 @@ function block_form_user_profile_form_al
     $account = $form['#user'];
     $rids = array_keys($account->roles);
     $result = db_query("SELECT DISTINCT b.* FROM {block} b LEFT JOIN {block_role} r ON b.module = r.module AND b.delta = r.delta WHERE b.status = 1 AND b.custom <> 0 AND (r.rid IN (:rids) OR r.rid IS NULL) ORDER BY b.weight, b.module", array(':rids' => $rids));
-    $form['block'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Personalize blocks'),
-      '#description' => t('Blocks consist of content or information that complements the main content of the page. Enable or disable optional blocks using the checkboxes below.'),
-      '#weight' => 3,
-      '#collapsible' => TRUE,
-      '#tree' => TRUE
-    );
+
+    $blocks = array();
     foreach ($result as $block) {
       $data = module_invoke($block->module, 'block_info');
       if ($data[$block->delta]['info']) {
-        $return = TRUE;
-        $form['block'][$block->module][$block->delta] = array(
+        $blocks[$block->module][$block->delta] = array(
           '#type' => 'checkbox',
           '#title' => check_plain($data[$block->delta]['info']),
           '#default_value' => isset($account->block[$block->module][$block->delta]) ? $account->block[$block->module][$block->delta] : ($block->custom == 1),
         );
       }
     }
-
-    if (!isset($return)) {
-      $form['block']['#access'] = FALSE;
+    // Only display the fieldset if there are any personalizable blocks.
+    if ($blocks) {
+      $form['block'] = array(
+        '#type' => 'fieldset',
+        '#title' => t('Personalize blocks'),
+        '#description' => t('Blocks consist of content or information that complements the main content of the page. Enable or disable optional blocks using the checkboxes below.'),
+        '#weight' => 3,
+        '#collapsible' => TRUE,
+        '#tree' => TRUE,
+      );
+      $form['block'] += $blocks;
     }
   }
 }
 
 /**
- * Implement hook_user_validate().
- */
-function block_user_validate(&$edit, $account, $category) {
-  if (empty($edit['block'])) {
-    $edit['block'] = array();
-  }
-  return $edit;
-}
-
-/**
  * Implement hook_form_FORM_ID_alter().
  */
 function block_form_system_themes_form_alter(&$form, &$form_state) {
Index: modules/openid/openid.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/openid/openid.module,v
retrieving revision 1.61
diff -u -p -r1.61 openid.module
--- modules/openid/openid.module	22 Sep 2009 07:50:16 -0000	1.61
+++ modules/openid/openid.module	4 Oct 2009 15:28:15 -0000
@@ -452,7 +452,7 @@ function openid_authentication($response
     }
     else {
       unset($form_state['values']['response']);
-      $account = user_save('', $form_state['values']);
+      $account = user_save(drupal_anonymous_user(), $form_state['values']);
       // Terminate if an error occurred during user_save().
       if (!$account) {
         drupal_set_message(t("Error saving user account."), 'error');
Index: modules/profile/profile.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile/profile.module,v
retrieving revision 1.276
diff -u -p -r1.276 profile.module
--- modules/profile/profile.module	22 Sep 2009 07:50:16 -0000	1.276
+++ modules/profile/profile.module	4 Oct 2009 15:28:15 -0000
@@ -210,17 +210,19 @@ function profile_block_view($delta = '')
 }
 
 /**
- * Implement hook_user_update().
+ * Implement hook_user_presave().
  */
-function profile_user_update(&$edit, $account, $category) {
-  return profile_save_profile($edit, $account, $category);
+function profile_user_presave(&$edit, $account, $category) {
+  if ($account->uid) {
+    profile_save_profile($edit, $account, $category);
+  }
 }
 
 /**
  * Implement hook_user_insert().
  */
 function profile_user_insert(&$edit, $account, $category) {
-  return profile_save_profile($edit, $account, $category, TRUE);
+  profile_save_profile($edit, $account, $category, TRUE);
 }
 
 /**
@@ -368,25 +370,23 @@ function _profile_form_explanation($fiel
  * Implement hook_form_alter().
  */
 function profile_form_alter(&$form, &$form_state, $form_id) {
-  if ($form_id == 'user_register' || $form_id == 'user_profile_form') {
-    $register = ($form['#user']->uid > 0 ? FALSE : TRUE);
-    $form = array_merge($form, profile_form_profile($form['#user'], $form['#user_category'], $register));
+  if (!($form_id == 'user_register' || $form_id == 'user_profile_form')) {
+    return;
   }
-}
-
-function profile_form_profile($account, $category, $register = FALSE) {
-  $result = _profile_get_fields($category, $register);
+  $form['#validate'][] = 'profile_user_form_validate';
+  $account = $form['#user'];
+  $result = _profile_get_fields($form['#user_category'], $form['#user_category'] == 'register');
   $weight = 1;
-  $fields = array();
   foreach ($result as $field) {
     $category = $field->category;
-    if (!isset($fields[$category])) {
-      $fields[$category] = array('#type' => 'fieldset', '#title' => check_plain($category), '#weight' => $weight++);
+    if (!isset($form[$category])) {
+      $form[$category] = array('#type' => 'fieldset', '#title' => check_plain($category), '#weight' => $weight++);
     }
     switch ($field->type) {
       case 'textfield':
       case 'url':
-        $fields[$category][$field->name] = array('#type' => 'textfield',
+        $form[$category][$field->name] = array(
+          '#type' => 'textfield',
           '#title' => check_plain($field->title),
           '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
           '#maxlength' => 255,
@@ -394,33 +394,40 @@ function profile_form_profile($account, 
           '#required' => $field->required,
         );
         if ($field->autocomplete) {
-          $fields[$category][$field->name]['#autocomplete_path'] = "profile/autocomplete/" . $field->fid;
+          $form[$category][$field->name]['#autocomplete_path'] = "profile/autocomplete/" . $field->fid;
         }
         break;
+
       case 'textarea':
-        $fields[$category][$field->name] = array('#type' => 'textarea',
+        $form[$category][$field->name] = array(
+          '#type' => 'textarea',
           '#title' => check_plain($field->title),
           '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
           '#description' => _profile_form_explanation($field),
           '#required' => $field->required,
         );
         break;
+
       case 'list':
-        $fields[$category][$field->name] = array('#type' => 'textarea',
+        $form[$category][$field->name] = array(
+          '#type' => 'textarea',
           '#title' => check_plain($field->title),
           '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
           '#description' => _profile_form_explanation($field),
           '#required' => $field->required,
         );
         break;
+
       case 'checkbox':
-        $fields[$category][$field->name] = array('#type' => 'checkbox',
+        $form[$category][$field->name] = array(
+          '#type' => 'checkbox',
           '#title' => check_plain($field->title),
           '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
           '#description' => _profile_form_explanation($field),
           '#required' => $field->required,
         );
         break;
+
       case 'selection':
         $options = $field->required ? array() : array('--');
         $lines = preg_split("/[\n\r]/", $field->options);
@@ -429,7 +436,8 @@ function profile_form_profile($account, 
             $options[$line] = $line;
           }
         }
-        $fields[$category][$field->name] = array('#type' => 'select',
+        $form[$category][$field->name] = array(
+          '#type' => 'select',
           '#title' => check_plain($field->title),
           '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
           '#options' => $options,
@@ -437,8 +445,10 @@ function profile_form_profile($account, 
           '#required' => $field->required,
         );
         break;
+
       case 'date':
-        $fields[$category][$field->name] = array('#type' => 'date',
+        $form[$category][$field->name] = array(
+          '#type' => 'date',
           '#title' => check_plain($field->title),
           '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '',
           '#description' => _profile_form_explanation($field),
@@ -447,7 +457,6 @@ function profile_form_profile($account, 
         break;
     }
   }
-  return $fields;
 }
 
 /**
@@ -461,25 +470,24 @@ function _profile_update_user_fields($fi
 }
 
 /**
- * Implement hook_user_validate().
+ * Form validation handler for the user register/profile form.
+ *
+ * @see profile_form_alter()
  */
-function profile_user_validate(&$edit, $account, $category) {
-  $result = _profile_get_fields($category);
+function profile_user_form_validate($form, &$form_state) {
+  $result = _profile_get_fields($form['#user_category'], $form['#user_category'] == 'register');
   foreach ($result as $field) {
-    if ($edit[$field->name]) {
-      if ($field->type == 'url') {
-        if (!valid_url($edit[$field->name], TRUE)) {
-          form_set_error($field->name, t('The value provided for %field is not a valid URL.', array('%field' => $field->title)));
-        }
+    if (!empty($form_state['values'][$field->name])) {
+      if ($field->type == 'url' && !valid_url($form_state['values'][$field->name], TRUE)) {
+        form_set_error($field->name, t('The value provided for %field is not a valid URL.', array('%field' => $field->title)));
       }
     }
     elseif ($field->required && !user_access('administer users')) {
       form_set_error($field->name, t('The field %field is required.', array('%field' => $field->title)));
     }
   }
-
-  return $edit;
 }
+
 /**
  * Implement hook_user_categories().
  */
Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.155
diff -u -p -r1.155 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	29 Sep 2009 15:31:15 -0000	1.155
+++ modules/simpletest/drupal_web_test_case.php	4 Oct 2009 15:28:15 -0000
@@ -866,7 +866,7 @@ class DrupalWebTestCase extends DrupalTe
     $edit['pass']   = user_password();
     $edit['status'] = 1;
 
-    $account = user_save('', $edit);
+    $account = user_save(drupal_anonymous_user(), $edit);
 
     $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login'));
     if (empty($account->uid)) {
Index: modules/trigger/trigger.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/trigger.module,v
retrieving revision 1.48
diff -u -p -r1.48 trigger.module
--- modules/trigger/trigger.module	19 Sep 2009 11:07:36 -0000	1.48
+++ modules/trigger/trigger.module	4 Oct 2009 15:28:15 -0000
@@ -136,11 +136,14 @@ function trigger_trigger_info() {
        ),
      ),
      'user' => array(
+       'user_presave' => array(
+         'label' => t('When either creating a new user account or updating an existing'),
+       ),
        'user_insert' => array(
-         'label' => t('After a user account has been created'),
+         'label' => t('After creating a new user account'),
        ),
        'user_update' => array(
-         'label' => t("After a user's profile has been updated"),
+         'label' => t('After updating a user account'),
        ),
        'user_delete' => array(
          'label' => t('After a user has been deleted'),
@@ -470,6 +473,13 @@ function trigger_user_logout($account) {
 }
 
 /**
+ * Implement hook_user_presave().
+ */
+function trigger_user_presave(&$edit, $account, $category) {
+  _trigger_user('user_presave', $edit, $account, $category);
+}
+
+/**
  * Implement hook_user_insert().
  */
 function trigger_user_insert(&$edit, $account, $category) {
Index: modules/trigger/tests/trigger_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/tests/trigger_test.module,v
retrieving revision 1.3
diff -u -p -r1.3 trigger_test.module
--- modules/trigger/tests/trigger_test.module	19 Sep 2009 11:07:37 -0000	1.3
+++ modules/trigger/tests/trigger_test.module	4 Oct 2009 15:28:15 -0000
@@ -35,6 +35,7 @@ function trigger_test_action_info() {
         'comment_insert',
         'comment_update',
         'comment_delete',
+        'user_presave',
         'user_insert',
         'user_update',
         'user_delete',
Index: modules/user/user.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.api.php,v
retrieving revision 1.13
diff -u -p -r1.13 user.api.php
--- modules/user/user.api.php	22 Sep 2009 07:50:16 -0000	1.13
+++ modules/user/user.api.php	4 Oct 2009 15:28:15 -0000
@@ -179,28 +179,6 @@ function hook_user_operations() {
 }
 
 /**
- * The user object has been updated and changed.
- *
- * Use this if (probably along with 'insert') if you want to reuse some
- * information from the user object.
- *
- * @param &$edit
- *   The array of form values submitted by the user.
- * @param $account
- *   The user object on which the operation is performed.
- * @param $category
- *   The active category of user information being edited.
- */
-function hook_user_after_update(&$edit, $account, $category) {
-  db_insert('user_changes')
-    ->fields(array(
-      'uid' => $account->uid,
-      'changed' => time(),
-    ))
-    ->execute();
-}
-
-/**
  * Retrieve a list of all user setting/information categories.
  *
  * @return
@@ -218,7 +196,32 @@ function hook_user_categories() {
 }
 
 /**
- * The user account is being added.
+ * A user account is about to be created or updated.
+ *
+ * The module should save its custom additions to the user object into the
+ * database and set the saved fields to NULL in $edit.
+ *
+ * @param &$edit
+ *   The array of form values submitted by the user.
+ * @param $account
+ *   The user object on which the operation is performed.
+ * @param $category
+ *   The active category of user information being edited.
+ */
+function hook_user_presave(&$edit, $account, $category) {
+  // For existing users, store our user property.
+  if ($account->uid) {
+    db_update('mytable')
+      ->fields(array('myfield' => $edit['myfield']))
+      ->condition('uid', $account->uid)
+      ->execute();
+    // Inform user_save() to ignore the value of our property.
+    $edit['myfield'] = NULL;
+  }
+}
+
+/**
+ * A user account was created.
  *
  * The module should save its custom additions to the user object into the
  * database and set the saved fields to NULL in $edit.
@@ -237,10 +240,33 @@ function hook_user_insert(&$edit, $accou
       'uid' => $account->uid,
     ))
     ->execute();
+  // Inform user_save() to ignore the value of our property.
   $edit['myfield'] = NULL;
 }
 
 /**
+ * A user account was updated.
+ *
+ * Use this if (probably along with 'insert') if you want to reuse some
+ * information from the user object.
+ *
+ * @param &$edit
+ *   The array of form values submitted by the user.
+ * @param $account
+ *   The user object on which the operation is performed.
+ * @param $category
+ *   The active category of user information being edited.
+ */
+function hook_user_update(&$edit, $account, $category) {
+  db_insert('user_changes')
+    ->fields(array(
+      'uid' => $account->uid,
+      'changed' => time(),
+    ))
+    ->execute();
+}
+
+/**
  * The user just logged in.
  *
  * @param &$edit
@@ -271,75 +297,6 @@ function hook_user_logout($account) {
 }
 
 /**
- * Modify the account before it gets saved.
- *
- * @param &$edit
- *   The array of form values submitted by the user.
- * @param $account
- *   The user object on which the operation is performed.
- * @param $category
- *   The active category of user information being edited.
- */
-function hook_user_submit(&$edit, $account, $category) {
-  if ($category == 'account') {
-    if (!empty($edit['picture_upload'])) {
-      $edit['picture'] = $edit['picture_upload'];
-    }
-    // Delete picture if requested, and if no replacement picture was given.
-    elseif (!empty($edit['picture_delete'])) {
-      $edit['picture'] = NULL;
-    }
-    // Remove these values so they don't end up serialized in the data field.
-    $edit['picture_upload'] = NULL;
-    $edit['picture_delete'] = NULL;
-
-    if (isset($edit['roles'])) {
-      $edit['roles'] = array_filter($edit['roles']);
-    }
-  }
-}
-
-/**
- * The user account is being changed.
- *
- * The module should save its custom additions to the user object into the
- * database and set the saved fields to NULL in $edit.
- *
- * @param &$edit
- *   The array of form values submitted by the user.
- * @param $account
- *   The user object on which the operation is performed.
- * @param $category
- *   The active category of user information being edited.
- */
-function hook_user_update(&$edit, $account, $category) {
-  db_update('mytable')
-    ->fields(array('myfield' => $edit['myfield']))
-    ->condition('uid', $account->uid)
-    ->execute();
-  $edit['myfield'] = NULL;
-}
-
-/**
- * The user account is about to be modified.
- *
- * The module should validate its custom additions to the user object,
- * registering errors as necessary.
- *
- * @param &$edit
- *   The array of form values submitted by the user.
- * @param $account
- *   The user object on which the operation is being performed.
- * @param $category
- *   The active category of user information being edited.
- */
-function hook_user_validate(&$edit, $account, $category) {
-  if ($category == 'mymodule' && empty($edit['myfield'])) {
-    form_set_error('myfield', t('Myfield is required.'));
-  }
-}
-
-/**
  * The user's account information is being displayed.
  *
  * The module should format its custom additions for display and add them to the
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1055
diff -u -p -r1.1055 user.module
--- modules/user/user.module	2 Oct 2009 14:49:10 -0000	1.1055
+++ modules/user/user.module	4 Oct 2009 15:30:18 -0000
@@ -340,8 +340,10 @@ function user_save($account, $edit = arr
   if (!isset($account->is_new)) {
     $account->is_new = empty($account->uid);
   }
+
+  user_module_invoke('presave', $edit, $account, $category);
+
   if (is_object($account) && !$account->is_new) {
-    user_module_invoke('update', $edit, $account, $category);
     $data = unserialize(db_query('SELECT data FROM {users} WHERE uid = :uid', array(':uid' => $account->uid))->fetchField());
     // Consider users edited by an administrator as logged in, if they haven't
     // already, so anonymous users can view the profile (if allowed).
@@ -388,15 +390,10 @@ function user_save($account, $edit = arr
     $edit['uid'] = $account->uid;
     // Save changes to the user table.
     $success = drupal_write_record('users', $edit, 'uid');
-    if (!$success) {
+    if ($success === FALSE) {
       // The query failed - better to abort the save than risk further
       // data loss.
-
-      // TODO: Fields change: I think this is a bug.  If no columns in
-      // the user table are changed, drupal_write_record returns
-      // FALSE because rowCount() (rows changed) is 0.  However,
-      // non-users data may have been changed, e.g. fields.
-      // return FALSE;
+      return FALSE;
     }
 
     // If the picture changed or was unset, remove the old one. This step needs
@@ -452,7 +449,7 @@ function user_save($account, $edit = arr
       _user_mail_notify($op, $user);
     }
 
-    user_module_invoke('after_update', $edit, $user, $category);
+    user_module_invoke('update', $edit, $user, $category);
   }
   else {
     // Allow 'created' to be set by the caller.
@@ -467,7 +464,7 @@ function user_save($account, $edit = arr
 
     $edit['mail'] = trim($edit['mail']);
     $success = drupal_write_record('users', $edit);
-    if (!$success) {
+    if ($success === FALSE) {
       // On a failed INSERT some other existing user's uid may be returned.
       // We must abort to avoid overwriting their account.
       return FALSE;
@@ -860,40 +857,186 @@ function user_user_view($account) {
 }
 
 /**
- * Implement hook_user_validate().
+ * Helper function to add default user account fields to user registration and edit form.
+ */
+function user_account_form(&$form, &$form_state) {
+  global $user;
+
+  $account = $form['#user'];
+  $register = ($form['#user']->uid > 0 ? FALSE : TRUE);
+
+  _user_password_dynamic_validation();
+  $admin = user_access('administer users');
+
+  $form['#validate'][] = 'user_account_form_validate';
+
+  // Account information.
+  $form['account'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Account information'),
+    '#weight' => -10,
+  );
+  // Only show name field on registration form or user can change own username.
+  $form['account']['name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Username'),
+    '#maxlength' => USERNAME_MAX_LENGTH,
+    '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'),
+    '#required' => TRUE,
+    '#attributes' => array('class' => array('username')),
+    '#default_value' => (!$register ? $account->name : ''),
+    '#access' => ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin),
+  );
+
+  $form['account']['mail'] = array(
+    '#type' => 'textfield',
+    '#title' => t('E-mail address'),
+    '#maxlength' => EMAIL_MAX_LENGTH,
+    '#description' => t('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.'),
+    '#required' => TRUE,
+    '#default_value' => (!$register ? $account->mail : ''),
+  );
+
+  // Display password field only for existing users or when user is allowed to
+  // assign a password during registration.
+  if (!$register) {
+    $form['account']['pass'] = array(
+      '#type' => 'password_confirm',
+      '#size' => 25,
+      '#description' => t('To change the current user password, enter the new password in both fields.'),
+    );
+  }
+  elseif (!variable_get('user_email_verification', TRUE) || $admin) {
+    $form['account']['pass'] = array(
+      '#type' => 'password_confirm',
+      '#size' => 25,
+      '#description' => t('Provide a password for the new account in both fields.'),
+      '#required' => TRUE,
+    );
+  }
+
+  if ($admin) {
+    $status = (isset($account->status) ? $account->status : 1);
+  }
+  else {
+    $status = (variable_get('user_register', 1) == 1);
+  }
+  $form['account']['status'] = array(
+    '#type' => 'radios',
+    '#title' => t('Status'),
+    '#default_value' => $status,
+    '#options' => array(t('Blocked'), t('Active')),
+    '#access' => $admin,
+  );
+
+  $roles = user_roles(TRUE);
+  // The disabled checkbox subelement for the 'authenticated user' role
+  // must be generated separately and added to the checkboxes element,
+  // because of a limitation in Form API not supporting a single disabled
+  // checkbox within a set of checkboxes.
+  // @todo This should be solved more elegantly. See issue #119038.
+  $checkbox_authenticated = array(
+    '#type' => 'checkbox',
+    '#title' => $roles[DRUPAL_AUTHENTICATED_RID],
+    '#default_value' => TRUE,
+    '#disabled' => TRUE,
+  );
+  unset($roles[DRUPAL_AUTHENTICATED_RID]);
+  if ($roles) {
+    $form['account']['roles'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Roles'),
+      '#default_value' => (!$register && isset($account->roles) ? array_keys($account->roles) : array()),
+      '#options' => $roles,
+      '#access' => user_access('administer permissions'),
+      DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated,
+    );
+  }
+
+  $form['account']['notify'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Notify user of new account'),
+    '#access' => $register && $admin,
+  );
+
+  // Signature.
+  $form['signature_settings'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Signature settings'),
+    '#weight' => 1,
+    '#access' => (!$register && variable_get('user_signatures', 0)),
+  );
+  $form['signature_settings']['signature'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Signature'),
+    '#default_value' => isset($account->signature) ? $account->signature : '',
+    '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
+  );
+
+  // Picture/avatar.
+  $form['picture'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Picture'),
+    '#weight' => 1,
+    '#access' => (!$register && variable_get('user_pictures', 0)),
+  );
+  $form['picture']['picture'] = array(
+    '#type' => 'value',
+    '#value' => isset($account->picture) ? $account->picture : NULL,
+  );
+  $form['picture']['picture_current'] = array(
+    '#markup' => theme('user_picture', $account),
+  );
+  $form['picture']['picture_delete'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Delete picture'),
+    '#access' => !empty($account->picture->fid),
+    '#description' => t('Check this box to delete your current picture.'),
+  );
+  $form['picture']['picture_upload'] = array(
+    '#type' => 'file',
+    '#title' => t('Upload picture'),
+    '#size' => 48,
+    '#description' => t('Your virtual face or picture. Maximum dimensions are %dimensions pixels 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', ''),
+  );
+  $form['#validate'][] = 'user_validate_picture';
+}
+
+/**
+ * Form validation handler for user_account_form().
  */
-function user_user_validate(&$edit, $account, $category) {
-  if ($category == 'account') {
-    $uid = isset($account->uid) ? $account->uid : FALSE;
-    // Validate the username when: new user account; or user is editing own account and can change username; or an admin user.
-    if (!$uid || ($GLOBALS['user']->uid == $uid && user_access('change own username')) || user_access('administer users')) {
-      if ($error = user_validate_name($edit['name'])) {
+function user_account_form_validate($form, &$form_state) {
+  if ($form['#user_category'] == 'account' || $form['#user_category'] == 'register') {
+    $account = $form['#user'];
+    // Validate new or changing username.
+    if (isset($form_state['values']['name'])) {
+      if ($error = user_validate_name($form_state['values']['name'])) {
         form_set_error('name', $error);
       }
-      elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(name) = LOWER(:name)", 0, 1, array(':uid' => $uid, ':name' => $edit['name']))->fetchField()) {
-        form_set_error('name', t('The name %name is already taken.', array('%name' => $edit['name'])));
+      elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(name) = LOWER(:name)", 0, 1, array(':uid' => $account->uid, ':name' => $form_state['values']['name']))->fetchField()) {
+        form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
       }
     }
 
     // Validate the e-mail address, and check if it is taken by an existing user.
-    if ($error = user_validate_mail($edit['mail'])) {
+    if ($error = user_validate_mail($form_state['values']['mail'])) {
       form_set_error('mail', $error);
     }
-    elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(mail) = LOWER(:mail)", 0, 1, array(':uid' => $uid, ':mail' => $edit['mail']))->fetchField()) {
+    elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(mail) = LOWER(:mail)", 0, 1, array(':uid' => $account->uid, ':mail' => $form_state['values']['mail']))->fetchField()) {
       // Format error message dependent on whether the user is logged in or not.
       if ($GLOBALS['user']->uid) {
-        form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $edit['mail'])));
+        form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $form_state['values']['mail'])));
       }
       else {
-        form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $edit['mail'], '@password' => url('user/password'))));
+        form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $form_state['values']['mail'], '@password' => url('user/password'))));
       }
     }
 
     // Make sure the signature isn't longer than the size of the database field.
     // Signatures are disabled by default, so make sure it exists first.
-    if (isset($edit['signature'])) {
+    if (isset($form_state['values']['signature'])) {
       $user_schema = drupal_get_schema('users');
-      if (strlen($edit['signature']) > $user_schema['fields']['signature']['length']) {
+      if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) {
         form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
       }
     }
@@ -901,10 +1044,10 @@ function user_user_validate(&$edit, $acc
 }
 
 /**
- * Implement hook_user_submit().
+ * Implement hook_user_presave().
  */
-function user_user_submit(&$edit, $account, $category) {
-  if ($category == 'account') {
+function user_user_presave(&$edit, $account, $category) {
+  if ($category == 'account' || $category == 'register') {
     if (!empty($edit['picture_upload'])) {
       $edit['picture'] = $edit['picture_upload'];
     }
@@ -916,6 +1059,7 @@ function user_user_submit(&$edit, $accou
     $edit['picture_upload'] = NULL;
     $edit['picture_delete'] = NULL;
 
+    // Prepare user roles.
     if (isset($edit['roles'])) {
       $edit['roles'] = array_filter($edit['roles']);
     }
@@ -1376,8 +1520,10 @@ function user_menu() {
 
   $items['user/%user/edit'] = array(
     'title' => 'Edit',
-    'page callback' => 'user_edit',
-    'page arguments' => array(1),
+    'title callback' => 'user_page_title',
+    'title arguments' => array(1),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('user_profile_form', 1),
     'access callback' => 'user_edit_access',
     'access arguments' => array(1),
     'type' => MENU_LOCAL_TASK,
@@ -1397,8 +1543,8 @@ function user_menu() {
         $items['user/%user_category/edit/' . $category['name']] = array(
           'title callback' => 'check_plain',
           'title arguments' => array($category['title']),
-          'page callback' => 'user_edit',
-          'page arguments' => array(1, 3),
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('user_profile_form', 1, 3),
           'access callback' => isset($category['access callback']) ? $category['access callback'] : 'user_edit_access',
           'access arguments' => isset($category['access arguments']) ? $category['access arguments'] : array(1),
           'type' => MENU_LOCAL_TASK,
@@ -1760,7 +1906,7 @@ function user_external_login_register($n
       'status' => 1,
       'access' => REQUEST_TIME
     );
-    $account = user_save('', $userinfo);
+    $account = user_save(drupal_anonymous_user(), $userinfo);
     // Terminate if an error occurred during user_save().
     if (!$account) {
       drupal_set_message(t("Error saving user account."), 'error');
@@ -1794,142 +1940,6 @@ function user_pass_rehash($password, $ti
   return md5($timestamp . $password . $login);
 }
 
-function user_edit_form(&$form, &$form_state) {
-  global $user;
-
-  $account = $form['#user'];
-  $register = ($form['#user']->uid > 0 ? FALSE : TRUE);
-
-  _user_password_dynamic_validation();
-  $admin = user_access('administer users');
-
-  // Account information:
-  $form['account'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Account information'),
-    '#weight' => -10,
-  );
-  // Only show name field when: registration page; or user is editing own
-  // account and can change username; or an admin user.
-  if ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin) {
-    $form['account']['name'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Username'),
-      '#maxlength' => USERNAME_MAX_LENGTH,
-      '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'),
-      '#required' => TRUE,
-      '#attributes' => array('class' => array('username')),
-    );
-    if (!$register) {
-      $form['account']['name']['#default_value'] = $account->name;
-    }
-  }
-  $form['account']['mail'] = array(
-    '#type' => 'textfield',
-    '#title' => t('E-mail address'),
-    '#maxlength' => EMAIL_MAX_LENGTH,
-    '#description' => t('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.'),
-    '#required' => TRUE,
-  );
-  if (!$register) {
-    $form['account']['mail']['#default_value'] = $account->mail;
-  }
-  if (!$register) {
-    $form['account']['pass'] = array(
-      '#type' => 'password_confirm',
-      '#description' => t('To change the current user password, enter the new password in both fields.'),
-      '#size' => 25,
-    );
-  }
-  elseif (!variable_get('user_email_verification', TRUE) || $admin) {
-    $form['account']['pass'] = array(
-      '#type' => 'password_confirm',
-      '#description' => t('Provide a password for the new account in both fields.'),
-      '#required' => TRUE,
-      '#size' => 25,
-    );
-  }
-  if ($admin) {
-    $form['account']['status'] = array(
-      '#type' => 'radios',
-      '#title' => t('Status'),
-      '#default_value' => isset($account->status) ? $account->status : 1,
-      '#options' => array(t('Blocked'), t('Active'))
-    );
-  }
-  if (user_access('administer permissions')) {
-    $roles = user_roles(TRUE);
-
-    // The disabled checkbox subelement for the 'authenticated user' role
-    // must be generated separately and added to the checkboxes element,
-    // because of a limitation in D6 FormAPI not supporting a single disabled
-    // checkbox within a set of checkboxes.
-    // TODO: This should be solved more elegantly. See issue #119038.
-    $checkbox_authenticated = array(
-      '#type' => 'checkbox',
-      '#title' => $roles[DRUPAL_AUTHENTICATED_RID],
-      '#default_value' => TRUE,
-      '#disabled' => TRUE,
-    );
-
-    unset($roles[DRUPAL_AUTHENTICATED_RID]);
-    if ($roles) {
-      $form['account']['roles'] = array(
-        '#type' => 'checkboxes',
-        '#title' => t('Roles'),
-        '#default_value' => isset($account->roles) ? array_keys($account->roles) : array(),
-        '#options' => $roles,
-        DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated,
-      );
-    }
-  }
-
-  // Signature:
-  if (variable_get('user_signatures', 0) && module_exists('comment') && !$register) {
-    $form['signature_settings'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Signature settings'),
-      '#weight' => 1,
-    );
-    $form['signature_settings']['signature'] = array(
-      '#type' => 'textarea',
-      '#title' => t('Signature'),
-      '#default_value' => isset($account->signature) ? $account->signature : '',
-      '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
-    );
-  }
-
-  // Picture/avatar:
-  if (variable_get('user_pictures', 0) && !$register) {
-    $form['picture'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Picture'),
-      '#weight' => 1,
-    );
-    $form['picture']['picture'] = array(
-      '#type' => 'value',
-      '#value' => isset($account->picture) ? $account->picture : NULL,
-    );
-    $form['picture']['picture_current'] = array(
-      '#markup' => theme('user_picture', $account),
-    );
-    $form['picture']['picture_delete'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Delete picture'),
-      '#access' => !empty($account->picture->fid),
-      '#description' => t('Check this box to delete your current picture.'),
-    );
-    $form['picture']['picture_upload'] = array(
-      '#type' => 'file',
-      '#title' => t('Upload picture'),
-      '#size' => 48,
-      '#description' => t('Your virtual face or picture. Maximum dimensions are %dimensions pixels 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', ''),
-    );
-    $form['#validate'][] = 'user_profile_form_validate';
-    $form['#validate'][] = 'user_validate_picture';
-  }
-}
-
 /**
  * Cancel a user account.
  *
@@ -3032,48 +3042,82 @@ function user_block_user_action(&$object
 }
 
 /**
+ * Form builder; the user registration form.
+ *
+ * @ingroup forms
+ * @see user_account_form()
+ * @see user_account_form_validate()
+ * @see user_account_form_submit()
+ * @see user_register_submit()
+ */
+function user_register($form, &$form_state) {
+  global $user;
+
+  $admin = user_access('administer users');
+
+  // If we aren't admin but already logged on, go to the user page instead.
+  if (!$admin && $user->uid) {
+    drupal_goto('user/' . $user->uid);
+  }
+
+  $form['#user'] = drupal_anonymous_user();
+  $form['#user_category'] = 'register';
+
+  // Start with the default user account fields.
+  user_account_form($form, $form_state);
+
+  if ($admin) {
+    // Redirect back to page which initiated the create request;
+    // usually admin/people/create.
+    $form_state['redirect'] = $_GET['q'];
+  }
+
+  // If the "account" fieldset is the only element at the top level, its
+  // borders are hidden for aesthetic reasons. We do not remove the fieldset but
+  // preserve the form structure so that modules implementing
+  // hook_form_FORM_ID_alter() know where to find the basic elements.
+  if (count(element_children($form)) == 1) {
+    $form['account']['#type'] = 'markup';
+  }
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Create new account'),
+    '#weight' => 30,
+  );
+
+  // Add the final user registration form submit handler.
+  $form['#submit'][] = 'user_register_submit';
+
+  return $form;
+}
+
+/**
  * Submit handler for the user registration form.
  *
  * This function is shared by the installation form and the normal registration form,
  * which is why it can't be in the user.pages.inc file.
  */
 function user_register_submit($form, &$form_state) {
-  global $base_url;
   $admin = user_access('administer users');
 
-  $mail = $form_state['values']['mail'];
-  $name = $form_state['values']['name'];
   if (!variable_get('user_email_verification', TRUE) || $admin) {
     $pass = $form_state['values']['pass'];
   }
   else {
     $pass = user_password();
-  };
-  $notify = isset($form_state['values']['notify']) ? $form_state['values']['notify'] : NULL;
-  $from = variable_get('site_mail', ini_get('sendmail_from'));
-  if (isset($form_state['values']['roles'])) {
-    // Remove unset roles.
-    $roles = array_filter($form_state['values']['roles']);
-  }
-  else {
-    $roles = array();
   }
+  $notify = !empty($form_state['values']['notify']);
 
-  if (!$admin && array_intersect(array_keys($form_state['values']), array('uid', 'roles', 'init', 'session', 'status'))) {
-    watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
-    $form_state['redirect'] = 'user/register';
-    return;
-  }
   // The unset below is needed to prevent these form values from being saved as
   // user data.
   unset($form_state['values']['form_token'], $form_state['values']['submit'], $form_state['values']['op'], $form_state['values']['notify'], $form_state['values']['form_id'], $form_state['values']['affiliates'], $form_state['values']['destination'], $form_state['values']['form_build_id']);
 
-  $merge_data = array('pass' => $pass, 'init' => $mail, 'roles' => $roles);
-  if (!$admin) {
-    // Set the user's status because it was not displayed in the form.
-    $merge_data['status'] = variable_get('user_register', 1) == 1;
-  }
-  $account = user_save('', array_merge($form_state['values'], $merge_data));
+  $form_state['values']['pass'] = $pass;
+  $form_state['values']['init'] = $form_state['values']['mail'];
+
+  $account = $form['#user'];
+  $account = user_save($account, $form_state['values']);
   // Terminate if an error occurred during user_save().
   if (!$account) {
     drupal_set_message(t("Error saving user account."), 'error');
@@ -3082,25 +3126,25 @@ function user_register_submit($form, &$f
   }
   $form_state['user'] = $account;
 
-  watchdog('user', 'New user: %name (%email).', array('%name' => $name, '%email' => $mail), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
+  watchdog('user', 'New user: %name (%email).', array('%name' => $form_state['values']['name'], '%email' => $form_state['values']['mail']), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
 
   // Add plain text password into user account to generate mail tokens.
   $account->password = $pass;
+
+  // New administrative account without notification.
   if ($admin && !$notify) {
     drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => url("user/$account->uid"), '%name' => $account->name)));
   }
-  elseif (!variable_get('user_email_verification', TRUE) && $account->status && !$admin) {
-    // No e-mail verification is required, create new user account, and login
-    // user immediately.
+  // No e-mail verification required; login user immediately.
+  elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
     _user_mail_notify('register_no_approval_required', $account);
     $form_state['uid'] = $account->uid;
     user_login_submit(array(), $form_state);
     drupal_set_message(t('Registration successful. You are now logged in.'));
     $form_state['redirect'] = '';
-    return;
   }
+  // No administrator approval required.
   elseif ($account->status || $notify) {
-    // Create new user account, no administrator approval required.
     $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
     _user_mail_notify($op, $account);
     if ($notify) {
@@ -3109,70 +3153,17 @@ function user_register_submit($form, &$f
     else {
       drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.'));
       $form_state['redirect'] = '';
-      return;
     }
   }
+  // Administrator approval required.
   else {
-    // Create new user account, administrator approval required.
     _user_mail_notify('register_pending_approval', $account);
     drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.<br />In the meantime, a welcome message with further instructions has been sent to your e-mail address.'));
     $form_state['redirect'] = '';
-    return;
-
   }
 }
 
 /**
- * Form builder; The user registration form.
- *
- * @ingroup forms
- * @see user_register_validate()
- * @see user_register_submit()
- */
-function user_register($form, &$form_state) {
-  global $user;
-
-  $admin = user_access('administer users');
-
-  // If we aren't admin but already logged on, go to the user page instead.
-  if (!$admin && $user->uid) {
-    drupal_goto('user/' . $user->uid);
-  }
-
-  $form['#user'] = drupal_anonymous_user();
-  $form['#user_category'] = 'register';
-
-  // Start with the default user account fields.
-  user_edit_form($form, $form_state);
-  if ($admin) {
-    $form['account']['notify'] = array(
-     '#type' => 'checkbox',
-     '#title' => t('Notify user of new account')
-    );
-    // Redirect back to page which initiated the create request;
-    // usually admin/people/create.
-    $form_state['redirect'] = $_GET['q'];
-  }
-
-  // If the "account" fieldset is the only element at the top level, its
-  // borders are hidden for aesthetic reasons. We do not remove the fieldset but
-  // preserve the form structure so that modules implementing
-  // hook_form_FORM_ID_alter() know where to find the basic elements.
-  if (count(element_children($form)) == 1) {
-    $form['account']['#type'] = 'markup';
-  }
-
-  $form['submit'] = array('#type' => 'submit', '#value' => t('Create new account'), '#weight' => 30);
-  $form['#validate'][] = 'user_register_validate';
-
-  return $form;
-}
-
-function user_register_validate($form, &$form_state) {
-  user_module_invoke('validate', $form_state['values'], $form_state['values'], 'account');
-}
-
-/**
  * Implementation of hook_modules_installed().
  */
 function user_modules_installed($modules) {
Index: modules/user/user.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.pages.inc,v
retrieving revision 1.57
diff -u -p -r1.57 user.pages.inc
--- modules/user/user.pages.inc	29 Sep 2009 15:31:17 -0000	1.57
+++ modules/user/user.pages.inc	4 Oct 2009 15:28:15 -0000
@@ -210,17 +210,12 @@ function template_preprocess_user_profil
 }
 
 /**
- * Menu callback; Present the form to edit a given user or profile category.
- */
-function user_edit($account, $category = 'account') {
-  drupal_set_title($account->name);
-  return drupal_get_form('user_profile_form', $account, $category);
-}
-
-/**
  * Form builder; edit a user account or one of their profile categories.
  *
  * @ingroup forms
+ * @see user_account_form()
+ * @see user_account_form_validate()
+ * @see user_account_form_submit()
  * @see user_profile_form_validate()
  * @see user_profile_form_submit()
  * @see user_cancel_confirm_form_submit()
@@ -232,23 +227,31 @@ function user_profile_form($form, &$form
   $form['#user_category'] = $category;
 
   if ($category == 'account') {
-    user_edit_form($form, $form_state);
+    user_account_form($form, $form_state);
   }
 
   // Attach field widgets.
   field_attach_form('user', $account, $form, $form_state);
 
-  $form['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#weight' => 30);
-
-  if (($account->uid == $user->uid && user_access('cancel account')) || user_access('administer users')) {
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#weight' => 30,
+  );
+  if ($category == 'account') {
     $form['cancel'] = array(
       '#type' => 'submit',
       '#value' => t('Cancel account'),
       '#weight' => 31,
       '#submit' => array('user_edit_cancel_submit'),
+      '#access' => ($account->uid == $user->uid && user_access('cancel account')) || user_access('administer users'),
     );
   }
 
+  $form['#validate'][] = 'user_profile_form_validate';
+  // Add the final user profile form submit handler.
+  $form['#submit'][] = 'user_profile_form_submit';
+
   return $form;
 }
 
@@ -258,14 +261,6 @@ function user_profile_form($form, &$form
 function user_profile_form_validate($form, &$form_state) {
   $edit = (object)$form_state['values'];
   field_attach_form_validate('user', $edit, $form, $form_state);
-  $edit = (array)$edit;
-  user_module_invoke('validate', $edit, $form['#user'], $form['#user_category']);
-  // 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', 'init', 'session'))) || (!user_access('administer permissions') && isset($form_state['values']['roles']))) {
-    watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
-    // set this to a value type field
-    form_set_error('category', t('Detected malicious attempt to alter protected user fields.'));
-  }
 }
 
 /**
@@ -279,7 +274,7 @@ function user_profile_form_submit($form,
   $edit = (object)$form_state['values'];
   field_attach_submit('user', $edit, $form, $form_state);
   $edit = (array)$edit;
-  user_module_invoke('submit', $edit, $account, $category);
+
   user_save($account, $edit, $category);
 
   // Clear the page cache because pages can contain usernames and/or profile information:
Index: modules/user/user.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.test,v
retrieving revision 1.62
diff -u -p -r1.62 user.test
--- modules/user/user.test	19 Sep 2009 10:54:35 -0000	1.62
+++ modules/user/user.test	4 Oct 2009 15:28:15 -0000
@@ -1224,7 +1224,7 @@ class UserSaveTestCase extends DrupalWeb
       'pass' => user_password(),
       'status' => 1,
     );
-    $user_by_return = user_save('', $user);
+    $user_by_return = user_save(drupal_anonymous_user(), $user);
     $this->assertTrue($user_by_return, t('Loading user by return of user_save().'));
 
     // Test if created user exists.
